home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / includes / game_play1.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  130KB  |  3,519 lines

  1. /*
  2.    game_play1.c -- game play module for Invasion Force
  3.  
  4.    This module handles game play: loading and saving games, getting orders
  5.    from the user, moving units, handling combat, and presenting various
  6.    menus for branching to the status reports, message system, etc.
  7.  
  8.    This source code is free.  You may make as many copies as you like.
  9. */
  10.  
  11. #include "global.h"
  12. #include <proto/layers.h>
  13.  
  14. #define CLEAR_WINDOW() SetRast(rast_port,0);zero_scrollers();display=0;
  15.  
  16. #define EXIT_GAME 1     // kluge values for flow control
  17. #define GAME_OVER 2
  18. #define UNIT_DONE 3     // I mainly use these to break out of nested functions
  19. #define UNIT_LOST 4
  20. #define GO_SURVEY 5
  21. #define EXIT_SURVEY   6
  22. #define GO_MOVEMENT   7
  23. #define END_TURN      8
  24. #define GAME_RESTORED 9
  25. #define GO_PRODUCTION 10
  26. #define CE_RESTART -17
  27.  
  28. int control_flag = 0;   // kluge variable to break out of nested funcs
  29.  
  30. #define INVISIBLE 0
  31. #define VISIBLE 1       // flag values, purely for more readable code
  32.  
  33. char game_filepath[108], game_filename[108];
  34.  
  35. struct Menu *move_menu_strip = NULL;
  36. struct Menu *vey_menu_strip = NULL;
  37. struct Menu *prod_menu_strip = NULL;
  38.  
  39. int PathCost=0;
  40.  
  41. struct PLayer roster[9];
  42. int player = 0;    // number of the current player
  43. int turn = 0;      // current turn number
  44. BOOL display;      // current status of player display
  45. int cursx, cursy;  // current location of the survey cursor
  46. char id_filetag[5];  // identifies temporary files
  47. struct MinList unit_list;   // master list of active game pieces
  48. struct BattleRecord battle;
  49.  
  50. char *prefix = "T:EMP2.";   // prefix for all Invasion force temporary files
  51.  
  52. // set_display_offsets() will set the xoffs and yoffs values to try and
  53. // center the specified col & row map position as closely as possible
  54.  
  55. void set_display_offsets(col,row)
  56. {
  57.    int overlap=(wrap?WRAP_OVERLAP:0);
  58.    int wd=width+2, ht=height+2;
  59.  
  60.    xoffs = col-(disp_wd >> 1);
  61.    if (xoffs<(0-overlap))
  62.       xoffs = (0-overlap);
  63.    if (xoffs>(wd-disp_wd+overlap))
  64.       xoffs = (wd-disp_wd+overlap);
  65.  
  66.    yoffs = row-(disp_ht >> 1);
  67.    if (yoffs<-overlap)
  68.       yoffs = -overlap;
  69.    if (yoffs>ht-disp_ht+overlap)
  70.       yoffs = ht-disp_ht+overlap;
  71.  
  72.    update_scrollers();
  73. }
  74.  
  75.  
  76. /*
  77.    count_units_at() -- return number of units at the specified map sector.
  78.    The AO can call this function to see how many units he has on a specified
  79.    sector, but should only call it for sectors he controls.  Using it to see
  80.    how many units the enemy has in a sector would be cheating!
  81. */
  82.  
  83. int count_units_at(col,row)
  84. int col,row;
  85. {
  86.    int ctr = 0;
  87.    struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  88.  
  89.    for (; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  90.       if (unit->col==col && unit->row==row)
  91.          ctr++;
  92.    return ctr;
  93. }
  94.  
  95.  
  96. // this is the case where the player has just seen a city, so we
  97. // must create an icon for it on his map
  98.  
  99. void add_city_to_player_map(player,metro)
  100. int player;
  101. struct City *metro;
  102. {
  103.    struct MapIcon *icon = AllocVec((long)sizeof(*icon),MEMF_CLEAR);
  104.  
  105.    if (icon==NULL) return;    // Will this mean Phantom Cities?
  106.  
  107.    // copy basic information
  108.    icon->col = metro->col;
  109.    icon->row = metro->row;
  110.    icon->type = CITY;
  111.    icon->owner = metro->owner;
  112.  
  113.    if (metro->owner==player)
  114.       icon->stacked = (count_units_at(icon->col,icon->row)>1);
  115.    else
  116.       icon->stacked = FALSE;
  117.    AddTail((struct List *)&PLAYER.icons,(struct Node *)icon);
  118. }
  119.  
  120.  
  121. // similar to function above
  122. // adds an icon to the map, this time for a unit
  123.  
  124. void add_icon_to_player_map(player,unit)
  125. int player;
  126. struct Unit *unit;
  127. {
  128.    struct MapIcon *icon = AllocVec((long)sizeof(*icon),MEMF_CLEAR);
  129.  
  130.    if (icon==NULL) return;
  131.  
  132.    // copy basic information
  133.    icon->col = unit->col;
  134.    icon->row = unit->row;
  135.    icon->type = unit->type;
  136.    icon->owner = unit->owner;
  137.    icon->token = ORDER_NONE;
  138.    if (unit->owner==player && unit->orders!=NULL)
  139.       icon->token = unit->orders->type;
  140.  
  141.    if (player==unit->owner)
  142.       icon->stacked = (count_units_at(icon->col,icon->row)>1);
  143.    else
  144.       icon->stacked = FALSE;
  145.    AddTail((struct List *)&PLAYER.icons,(struct Node *)icon);
  146. }
  147.  
  148.  
  149. /*
  150.    submarines cannot see land or air units
  151.    when a hex is being explored, this function determines whether
  152.    a land or air unit is seen either by a sub or by some other adjacent
  153.    unit or city; returns TRUE if seen, FALSE otherwise
  154. */
  155. BOOL seenby_subP(player,targ)
  156. int player;
  157. struct Unit *targ;
  158. {
  159.    int col=targ->col, row=targ->row;
  160.    int num_hexes=adjacent(col,row);
  161.    struct City *metro;
  162.    struct Unit *unit;
  163.    int ctr;
  164.  
  165.    if (targ->owner==player)
  166.       return TRUE;
  167.  
  168.    if (wishbook[targ->type].ship_flag)  // subs can always see ships
  169.       return TRUE;
  170.  
  171.    // search adjacent hexes for cities or units that can spot the target
  172.    for (ctr=0; ctr<num_hexes; ctr++) {
  173.       col = hexlist[ctr].col;
  174.       row = hexlist[ctr].row;
  175.       if (metro = city_hereP(col,row))
  176.          if (metro->owner==player)
  177.             return TRUE;
  178.       for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  179.          if (unit->col==col && unit->row==row && unit->owner==player)
  180.             if (unit->type!=SUB)
  181.                return TRUE;
  182.    OD
  183.    return FALSE;  // nobody spotted him
  184. }
  185.  
  186.  
  187. // determine whether a submarine at the given position is visible
  188. // returns TRUE if the unit "sub" can be seen by any cities or other
  189. // units owned by "player", otherwise FALSE
  190.  
  191. BOOL sub_seenP(player,sub)
  192. int player;    // the player who is exploring
  193. struct Unit *sub;
  194. {
  195.    int col=sub->col, row=sub->row;
  196.    int terrain=get(t_grid,col,row);
  197.    int num_hexes=adjacent(col,row);
  198.    struct City *metro;
  199.    struct Unit *unit;
  200.    int ctr;
  201.  
  202.    if (sub->owner==player)
  203.       return TRUE;
  204.  
  205.    if (terrain==HEX_SHALLOWS)   // subs always visible in shallow water
  206.       return TRUE;
  207.  
  208.    // search adjacent hexes for cities or units that can spot the sub
  209.    for (ctr=0; ctr<num_hexes; ctr++) {
  210.       col = hexlist[ctr].col;
  211.       row = hexlist[ctr].row;
  212.       if (metro = city_hereP(col,row))
  213.          if (metro->owner==player)
  214.             return TRUE;
  215.       for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  216.          if (unit->col==col && unit->row==row && unit->owner==player)
  217.             if (unit->type==CRUISER || unit->type==DESTROYER || unit->type==SUB)
  218.                return TRUE;
  219.    OD
  220.    return FALSE;  // nobody spotted him
  221. }
  222.  
  223.  
  224. /*
  225.    recon() determines if a given city is vulnerable to recon by the current
  226.    player's aircraft (in other words, if the player has a plane in an
  227.    adjacent hex to see what the city is producing); if the city is
  228.    vulnerable to recon, we will store the production type in city.recon[]
  229. */
  230.  
  231. void recon(metro)
  232. struct City *metro;
  233. {
  234.    int hexes, ctr;
  235.  
  236.    if (metro==NULL)
  237.       return;
  238.    if (metro->owner==player || metro->owner==0)
  239.       return;
  240.  
  241.    hexes = adjacent(metro->col,metro->row);   // build list of adjacent hexes
  242.    for (ctr=0;ctr<hexes;ctr++) {
  243.       struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  244.  
  245.       // search this hex for recon aircraft (currently only fighters)
  246.       for (;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  247.          if (unit->col==hexlist[ctr].col && unit->row==hexlist[ctr].row)
  248.             if (unit->owner==player && unit->type==FIGHTER) {
  249.                metro->recon[player] = metro->unit_type;
  250.                return;
  251.             FI
  252.    }
  253. }
  254.  
  255.  
  256. // very important -- function to explore a hex for a certain player
  257.  
  258. void explore_hex(player,col,row,visible,forced)
  259. int player, col, row, visible, forced;
  260. { // explore an individual hex
  261.  
  262.    // try wrapping the value around onto the map
  263.    wrap_coords(&col,&row);
  264.  
  265.    // now see what terrain is here
  266.    put(PLAYER.map,col,row,get(t_grid,col,row));
  267.  
  268.    // also roads
  269.    if (get_flags(t_grid,col,row)&ROAD)
  270.       put_flags(PLAYER.map,col,row,get_flags(PLAYER.map,col,row)|ROAD);
  271.    else
  272.       put_flags(PLAYER.map,col,row,get_flags(PLAYER.map,col,row)&(~ROAD));
  273.  
  274.    /* clear any outdated icons from the player's map, this position */
  275.    {
  276.       BOOL zapped;
  277.  
  278.       do {
  279.          struct MapIcon *icon = (struct MapIcon *)PLAYER.icons.mlh_Head;
  280.  
  281.          zapped = FALSE;
  282.          for (; icon->inode.mln_Succ; icon = (struct MapIcon *)icon->inode.mln_Succ) {
  283.             if (icon->col==col && icon->row==row) {
  284.                Remove((struct Node *)icon);
  285.                FreeVec(icon);
  286.                zapped = TRUE;
  287.                break;
  288.             FI
  289.          OD
  290.       } while (zapped);
  291.    }
  292.  
  293.    // update any city that might be here
  294.    {
  295.       struct City *metro = (struct City *)city_list.mlh_Head;
  296.       for ( ; metro->cnode.mln_Succ; metro = (struct City *)metro->cnode.mln_Succ)
  297.          if (metro->col==col && metro->row==row) {
  298.             add_city_to_player_map(player,metro);
  299.             recon(metro);   // try to recon the city
  300.             goto eh_final_update;  /* don't display military units if a city is here */
  301.          FI
  302.    }
  303.  
  304.    // finally, search for new up-to-date pieces and map them
  305.    {
  306.       struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  307.       BOOL mapped=FALSE;
  308.  
  309.       for (; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  310.          if (unit->row==row && unit->col==col) { // found one!
  311.             if (unit->owner!=player)
  312.                clear_orders(unit);  // enemy units come off sentry now
  313.             if (unit->ship)  continue;    /* don't map units on board ships */
  314.             if (unit->type==SUB)    // subs sometimes not visible, of course
  315.                if (sub_seenP(player,unit)==FALSE)
  316.                   continue;
  317.             if (seenby_subP(player,unit)==FALSE && forced==FALSE)
  318.                continue;
  319.             if (mapped==FALSE) {
  320.                mapped = TRUE;  // only map the first one
  321.                add_icon_to_player_map(player,unit);
  322.             FI
  323.          FI
  324.    }
  325.  
  326.  eh_final_update:
  327.    if (visible)  //NOTE: this is now a BOOLEAN value!
  328.       GP_update_hex_display(col,row);
  329. }
  330.  
  331.  
  332. /*
  333.    given a hex, explore_at_hex() will explore it and everyplace
  334.    directly adjacent -- this is the normal way that exploration happens
  335.    in Invasion Force
  336. */
  337. void explore_at_hex(player,col,row,visible,forced)
  338. int player, col, row, visible, forced;
  339. { // explore everything in and adjacent to a given hex
  340.    explore_hex(player,col,row,visible,forced);
  341.    explore_hex(player,col-1,row,visible,forced);
  342.    explore_hex(player,col+1,row,visible,forced);
  343.    explore_hex(player,col,row-1,visible,forced);
  344.    explore_hex(player,col,row+1,visible,forced);
  345.    if (row%2) {  /* i.e. if it's an odd-numbered column */
  346.       explore_hex(player,col+1,row-1,visible,forced);
  347.       explore_hex(player,col+1,row+1,visible,forced);
  348.    } else {
  349.       explore_hex(player,col-1,row-1,visible,forced);
  350.       explore_hex(player,col-1,row+1,visible,forced);
  351.    FI
  352. }
  353.  
  354.  
  355. // gives the city to a player, either his home city at the start of the game,
  356. // or else a city he has conquered with a military unit -- and includes a
  357. // call to examine_city() in the "status.c" module
  358.  
  359. void conquer_city(taken_city)
  360. struct City *taken_city;
  361. {  // take a city
  362.    int prior_owner=taken_city->owner;
  363.    BOOL vis=Bool(PLAYER.show&SHOW_GRP);  // visibility
  364.  
  365.    /*
  366.       When this function is called, the map should *already* be displayed
  367.       and scrolled to the proper location, so the city is positioned on the
  368.       screen; conquer_city() does NOT do this automatically.
  369.    */
  370.    taken_city->owner = player;   // city mine now!
  371.    // update the map icons for both old and new owners
  372.    if (prior_owner!=0)     // no prior owner of neutral cities
  373.       explore_hex(prior_owner,taken_city->col,taken_city->row,INVISIBLE,TRUE);
  374.    explore_at_hex(player,taken_city->col,taken_city->row,vis,TRUE);
  375.    if (vis) {
  376.       // do this so the user can see which city he is looking at
  377.       save_hex_graphics(taken_city->col,taken_city->row,0);    // blit the background to a safe place
  378.       plot_mapobject(taken_city->col,taken_city->row,MAP_MARKER);
  379.    FI
  380.    // set production to -1 to show that we will be restarting
  381.    // production from zilch
  382.    taken_city->unit_type = -1;
  383.    taken_city->unit_wip = 0;
  384.    if (ISHUMAN(PLAYER.type)) {
  385.       // create requester -- let him select production
  386.       examine_city(taken_city);
  387.       // unmark the city on screen
  388.       restore_hex_graphics(taken_city->col,taken_city->row,0);
  389.    FI
  390.    if (NONHUMAN(PLAYER.type)) {
  391.       // we call the computer player version of selecting production
  392.       set_automated_production(taken_city);
  393.    FI
  394. }
  395.  
  396.  
  397. // return the player who controls the specified hex, or 0 if nobody does
  398. int hex_owner(col,row)
  399. int col,row;
  400. {
  401.    struct City *metro;
  402.    struct Unit *unit=(struct Unit *)unit_list.mlh_Head;
  403.  
  404.    // search the cities
  405.    if (metro = city_hereP(col,row))
  406.       return (int)metro->owner;
  407.  
  408.    // search the military units
  409.    for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  410.       if (unit->col==col && unit->row==row)
  411.          return (int)unit->owner;
  412.  
  413.    return 0;
  414. }
  415.  
  416.  
  417. // quick macro to see if a unit has been killed
  418. #define ALIVE(a) (a->damage<wishbook[a->type].hitpoints)
  419.  
  420.  
  421. /***
  422.    choose_defender() == select a unit to defend a hex that has come under attack
  423.    This is supposed to handle the complexities of combat that were introduced
  424.    when I implemented stacking rules, particularly when different kinds of
  425.    units are stacked in the hex that is under attack
  426. ***/
  427.  
  428. struct Unit *choose_defender(attacker,targx,targy)
  429. struct Unit *attacker;
  430. int targx, targy;
  431. {
  432.    struct Unit *unit, *defender=NULL;
  433.    static int aapt[] = { 4, 5, 3,  2,  1, 6, 7, 8, 10, 11, 9, 99, 99 };
  434.    static int sapt[] = { 1, 2, 3, 11, 10, 4, 5, 6,  8,  9, 7, 99, 99 };
  435.    int *table = sapt;
  436.    int priority = 99;
  437.    int terrain = get(t_grid,targx,targy);
  438.  
  439.    // first determine if I should use the AAPT or the SAPT
  440.    if (attacker->type==FIGHTER)
  441.       table = aapt;
  442.  
  443.    for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  444.       if (unit->col==targx && unit->row==targy)
  445.          if (table[unit->type]<priority) {
  446.             // I have to throw out any ground units that are on board a ship
  447.             if (OCEAN_P(terrain) && (unit->type==RIFLE || unit->type==ARMOR))
  448.                continue;
  449.             // also throw out any fighters that are landed (not overflying)
  450.             if (unit->ship)
  451.                continue;
  452.             defender = unit;
  453.             priority = table[unit->type];
  454.          FI
  455.  
  456.    if (defender==NULL)
  457.       print("ERROR: choose_defender() failed to find defending unit for combat!\n");
  458.  
  459.    return defender;
  460. }
  461.  
  462.  
  463. /***
  464.    attack_hex() == universal attack function
  465.    All attacks in the game are targetted against a hex, not against specific
  466.    enemy units.  This function calculates the defensive strength of the target
  467.    hex and resolves the conflict.  The outcome is stored in "control_flag" and
  468.    in the combat report.
  469. ***/
  470.  
  471. void attack_hex(attacker,targx,targy)
  472. struct Unit *attacker;
  473. int targx, targy;
  474. {
  475.    int att_player=attacker->owner, def_player=0;// attacking and defending players
  476.    int def_terrain;
  477.    int attfact, deffact, movement_cost;
  478.    struct Unit *defender=NULL;
  479.    struct Unit militia;
  480.    BOOL bombardment=FALSE;
  481.    BOOL white_flag=FALSE;
  482.    ULONG num_blows, bit_blows;
  483.    struct City *captured=NULL;
  484.  
  485.    clear_orders(attacker);    // just to make sure
  486.  
  487.    /*
  488.       First part of this function is devoted to detecting various kinds of
  489.       invalid attacks and weeding them out before we get to the good stuff.
  490.    */
  491.  
  492.    // has the unit already attacked something this turn?
  493.    // fighters can make multiple attacks per turn; other cannot
  494. // I have disabled this until I get a beter idea how it should work
  495. //   if (attacker->type!=FIGHTER && attacker->attacks>0) {
  496. //      tell_user2("That unit has already attacked once this turn.",FALSE,DONK_SOUND);
  497. //      return;     // other units cannot
  498. //   FI
  499.  
  500.    if (city_hereP(targx,targy))
  501.       switch (attacker->type) {
  502.          case BOMBER:   // bomber can attack city, but not capture it
  503.             bombardment = TRUE;
  504.          case ARMOR:
  505.          case RIFLE:
  506.          case AIRCAV:
  507.             break;      // these units can attack cities
  508.          default:       // other units cannot
  509.             if (PLAYER.show&SHOW_REQ) {
  510.                playSound(DONK_SOUND,PLAYER.snd_vol);
  511.                alert(map_window,"Information...","That unit cannot attack a city.","Okay");
  512.             }
  513.             return;
  514.       };
  515.  
  516.    // bombers and aircav cannot attack any hex containing airborne craft
  517.    // aircraft on carriers or in cities or airfields are not considered airborne
  518.    if (attacker->type==BOMBER || attacker->type==AIRCAV)
  519.       if (city_hereP(targx,targy)==NULL) {
  520.          struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  521.          for (;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  522.             if (unit->ship==NULL && unit->col==targx && unit->row==targy)
  523.                switch (unit->type) {
  524.                   case FIGHTER:
  525.                   case BOMBER:
  526.                   case AIRCAV:
  527.                      if (PLAYER.show&SHOW_REQ) {
  528.                         playSound(DONK_SOUND,PLAYER.snd_vol);
  529.                         alert(map_window,"Information...","Bombers or air cav cannot attack other aircraft.","Okay");
  530.                      }
  531.                      return;
  532.                }
  533.       FI
  534.  
  535.    def_terrain = get(t_grid,targx,targy);
  536.    if (city_hereP(targx,targy)) {
  537.       def_terrain = HEX_CITY;
  538.       // create a dummy "militia unit" to defend the city
  539.       militia.col = targx;
  540.       militia.row = targy;
  541.       militia.owner = city_hereP(targx,targy)->owner;
  542.       militia.type = RIFLE;
  543.       militia.damage = 0;
  544.       militia.orders = NULL;
  545.       defender = &militia;
  546.       def_player = city_hereP(targx,targy)->owner;
  547.       // set flag to show we are using a militia unit
  548.       white_flag = TRUE;
  549.    };
  550.  
  551.    /* find the defending UNIT in the hex -- the unit we are using to */
  552.    /* determine the defender's strength, etc. */
  553.    if (defender==NULL)
  554.       defender = choose_defender(attacker,targx,targy);
  555.  
  556.    if (defender) {
  557.       clear_orders(defender);    // just to make sure
  558.       if( def_player == 0 )
  559.           def_player = defender->owner;
  560.    } else {
  561.       print("Error finding defender for combat!\n");
  562.       return;
  563.    FI
  564.  
  565.    // when a fighter attacks a hex containing both aircraft and other units,
  566.    // I set the bombardment flag to show the fighter should not enter the
  567.    // hex, even if victorious
  568.    if (attacker->type==FIGHTER) {
  569.       BOOL air=FALSE, surface=FALSE;
  570.       struct Unit *unit;
  571.       for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  572.          if (unit->col==targx && unit->row==targy) {
  573.             if (wishbook[unit->type].range>0)   // aircraft
  574.                air=TRUE;
  575.             else
  576.                surface=TRUE;
  577.             if (air==TRUE && surface==TRUE) {
  578.                bombardment = TRUE;
  579.                break;
  580.             FI
  581.          FI
  582.    FI
  583.  
  584.    // most ships cannot attack ground forces; only cruisers and battleships can
  585.    if (movement_cost_table[attacker->type][def_terrain]<0)
  586.       switch (attacker->type) {
  587.          case TRANSPORT:
  588.          case SUB:
  589.          case DESTROYER:
  590.          case CARRIER:
  591.             if (PLAYER.show&SHOW_REQ) {
  592.                playSound(DONK_SOUND,PLAYER.snd_vol);
  593.                sprintf(foo,"The %s cannot bombard land targets.",
  594.                   wishbook[attacker->type].name);
  595.                (void)rtEZRequestTags(foo,"Oops!",NULL,NULL,
  596.                   RT_Window,        map_window,
  597.                   RT_ReqPos,        REQPOS_CENTERSCR,
  598.                   RT_LockWindow,    TRUE,
  599.                   RTEZ_Flags,       EZREQF_CENTERTEXT,
  600.                   TAG_DONE );
  601.                }
  602.             return;
  603.          case CRUISER:
  604.          case BATTLESHIP:
  605.             bombardment = TRUE;
  606.       }
  607.  
  608.    // riflemen or armor can bombard ships unless aircraft are protecting them
  609.    if (OCEAN_P(def_terrain))
  610.       if (attacker->type==RIFLE || attacker->type==ARMOR) {
  611.          /*
  612.             At this point we might have ground forces trying to attack something
  613.             in a HEX_SHALLOWS terrain with a bridge over it.
  614.  
  615.             Otherwise the ground forces must be trying to bombard an ocean hex.
  616.             This would mean it's attacking a hex with ships or aircraft or both.
  617.          */
  618.          BOOL ship_here=FALSE, aircraft_here=FALSE, bridge_here=FALSE;
  619.          struct Unit *sunit;
  620.  
  621.          // check for roads first
  622.          if (get_flags(t_grid,attacker->col,attacker->row)& \
  623.          get_flags(t_grid,defender->col,defender->row)&ROAD)
  624.             bridge_here = TRUE;
  625.  
  626.          for (sunit=(struct Unit *)unit_list.mlh_Head;sunit->unode.mln_Succ;sunit=(struct Unit *)sunit->unode.mln_Succ)
  627.             if (wishbook[sunit->type].ship_flag && sunit->col==targx && sunit->row==targy) {
  628.                ship_here = TRUE;
  629.                break;
  630.             FI
  631.          for (sunit=(struct Unit *)unit_list.mlh_Head;sunit->unode.mln_Succ;sunit=(struct Unit *)sunit->unode.mln_Succ)
  632.             if (wishbook[sunit->type].range>0 && sunit->col==targx && sunit->row==targy) {
  633.                aircraft_here = TRUE;
  634.                break;
  635.             FI
  636.          /*
  637.             There are three possibilities for the target ocean hex.  It could have
  638.             ships, aircraft, or both ships and aircraft.  Each calls for a slightly
  639.             different response.
  640.          */
  641.          if (aircraft_here && ship_here) {
  642.             if (PLAYER.show & SHOW_REQ) {
  643.                 playSound(DONK_SOUND,PLAYER.snd_vol);
  644.                 (void)rtEZRequestTags(\
  645.                    "Enemy air cover prevents your ground forces\nfrom bombarding ships there.",
  646.                    "Drat!",NULL,NULL,
  647.                    RT_Window,        map_window,
  648.                    RT_ReqPos,        REQPOS_CENTERSCR,
  649.                    RT_LockWindow,    TRUE,
  650.                    RTEZ_Flags,       EZREQF_CENTERTEXT,
  651.                    TAG_DONE );
  652.                 }
  653.             return;
  654.          FI
  655.          if (aircraft_here) {
  656.             if (PLAYER.show & SHOW_REQ) {
  657.                 playSound(DONK_SOUND,PLAYER.snd_vol);
  658.                 (void)rtEZRequestTags(\
  659.                    "Ground forces cannot attack aircraft that are over water.",
  660.                    "Drat!",NULL,NULL,
  661.                    RT_Window,        map_window,
  662.                    RT_ReqPos,        REQPOS_CENTERSCR,
  663.                    RT_LockWindow,    TRUE,
  664.                    RTEZ_Flags,       EZREQF_CENTERTEXT,
  665.                    TAG_DONE );
  666.                 }
  667.             return;
  668.          FI
  669.          // if weve made it this far, there must only be ships
  670.          if (wishbook[defender->type].ship_flag)
  671.             bombardment = TRUE;
  672.       FI
  673.  
  674.    // determine whether the attacking unit has enough movement value left
  675.    // to carry out the assault, i.e. enough to enter the enemy hex area
  676.    if (def_terrain==HEX_CITY || bombardment==TRUE)
  677.       movement_cost = 60;
  678.    else
  679.       movement_cost = movement_cost_table[attacker->type][def_terrain];
  680.  
  681.    if (movement_cost < attacker->move) {
  682.       attacker->move -= movement_cost;
  683.       if (attacker->move<11) {
  684.          attacker->move = 0;
  685.          control_flag = UNIT_DONE;
  686.       }
  687.    } else {    // he only has enough movement for a /chance/ at this
  688.       int chance = attacker->move;
  689.  
  690.       attacker->move = 0;
  691.       control_flag = UNIT_DONE;
  692.       if (RangeRand(movement_cost)>chance) {
  693.          if( PLAYER.soundfx == SOUND_ALL ) {
  694.              // Only play the sound if the player wants stuff shown
  695.              playSound(SMASH_SOUND,PLAYER.snd_vol);
  696.          }
  697.          return;     // his movement attempt failed
  698.       FI
  699.    }
  700.  
  701.    /*
  702.       Bombardment, in the context of this game, means that a unit is attacking
  703.       a hex area that it cannot move onto, such as a cruiser firing upon rifle
  704.       units.  The only difference between bombardment and normal combat is that
  705.       the victorious attacking unit does not move.
  706.    */
  707.  
  708.    /*
  709.       Now we take time out to initialize and record basic information
  710.       into the structure for the combat report.
  711.    */
  712.    battle.turn = turn;
  713.    battle.att_x = attacker->col;
  714.    battle.att_y = attacker->row;
  715.    battle.att_owner = attacker->owner;
  716.    battle.att_type = attacker->type;
  717.    battle.def_x = defender->col;
  718.    battle.def_y = defender->row;
  719.    battle.def_owner = defender->owner;
  720.    battle.white_icon = white_flag;
  721.    battle.def_type = defender->type;
  722.    battle.winner = 0;
  723.    battle.casualties = 0;
  724.    battle.bombardment = bombardment;
  725.    // always seen by defender; we can add others flags later
  726.    battle.seen_by = mask(battle.def_owner);
  727.    battle.blows = 0L;
  728.  
  729.    // track number of attacks made by this unit
  730.    attacker->attacks++;
  731.  
  732.    // move the unit
  733.    if (bombardment==FALSE) {
  734.       attacker->col = targx;
  735.       attacker->row = targy;
  736.    FI
  737.    // keep survey mode up to date
  738.    cursx = attacker->col;  cursy = attacker->row;
  739.    if (attacker->ship) {     // he must be attacking from on board a ship
  740.       attacker->ship->cargo--;
  741.       attacker->ship->weight -= cargo_weight(attacker->type);
  742.       if (attacker->ship->type==TRANSPORT)
  743.          attacker->move = 0;   // unloading from a transport expends movement
  744.       attacker->ship=NULL;  // so we unlink him from it
  745.    FI
  746.    if (attacker->type==TRANSPORT && attacker->cargo>0) {   // ship moves, cargo moves
  747.       struct Unit *cargo=(struct Unit *)unit_list.mlh_Head;
  748.       for (; cargo->unode.mln_Succ; cargo=(struct Unit *)cargo->unode.mln_Succ)
  749.          if (cargo->ship==attacker) {
  750.             cargo->col=targx;  cargo->row=targy;
  751.             clear_orders(cargo);
  752.          }
  753.    }
  754.    if (wishbook[attacker->type].range>0)   // units that need fuel, i.e. aircraft
  755.       attacker->fuel--;
  756.  
  757.    /*
  758.       This function looks at an attacking unit and a defending unit and adds up
  759.       various modifiers based on the types of units, the terrain, the handicap
  760.       of the players, etc.  It sticks the results in attfact and deffact.
  761.    */
  762.    NCS_combat_mods(attacker,defender,&attfact,&deffact);
  763.  
  764.    /*
  765.       battle actually takes place; calculate attacks, damage
  766.       record combat reports, display battle action and sounds
  767.       NOTE: battle always begins with two visible blows that have no game
  768.       effect, but serve to establish visually who is attacking who
  769.    */
  770.    num_blows = 2L;    bit_blows = 2L;
  771.    while (ALIVE(attacker) && ALIVE(defender)) {
  772.       int attval, defval;
  773.       static int strength[] = { 1, 1, 1, 2, 1, 1, 3, 1, 2, 3, 1, 1 };
  774.  
  775.       /*
  776.          strength is the amount of damage done by a hit from the unit
  777.          infantry --- 1
  778.          armor ------ 1
  779.          aircav ----- 1
  780.          bomber ----- 2
  781.          fighter ---- 1
  782.          transport -- 1
  783.          sub -------- 3
  784.          destroyer -- 1
  785.          cruiser ---- 2
  786.          battleship - 3
  787.          carrier ---- 1
  788.       */
  789.  
  790.       /*
  791.          Here is the NCS (New Combat System) which is based on simulated roll of
  792.          three six-sided dice (3d6) for each player.
  793.  
  794.             attval = 3d6 + attfact
  795.             defval = 3d6 + deffact
  796.  
  797.          And whoever's total is higher wins the round of combat.
  798.       */
  799.  
  800.       do {
  801.          attval = 3 + RangeRand(6L) + RangeRand(6L) + RangeRand(6L) + attfact;
  802.          defval = 3 + RangeRand(6L) + RangeRand(6L) + RangeRand(6L) + deffact;
  803.       } while (attval==defval);     // we don't allow draws
  804.  
  805.       if (num_blows<24L) {
  806.          num_blows++;
  807.          bit_blows <<= 1;  // make way for the bit
  808.          if (attval>defval)
  809.             bit_blows |= 1L;   // this bit shows who was hit
  810.       FI
  811.       if (attval>defval)
  812.          defender->damage += strength[attacker->type];
  813.       else {
  814.          attacker->damage += strength[defender->type];
  815.  
  816.          /*
  817.             If the attacker is a ship, and it's taken enough damage to slow its
  818.             speed, then I want to go back and retro-actively reduce its rate for
  819.             THIS turn.  In other words, if my destroyer (speed 3) attacks on its
  820.             first move of this turn, and it takes two points of damage, it's
  821.             speed should be *immediately* reduced to one -- and that one has
  822.             already been expended in the attack!
  823.          */
  824.          if (unit_speed(attacker)<wishbook[attacker->type].speed)
  825.             attacker->move = 0;
  826.       FI
  827.    OD
  828.    battle.blows = (num_blows<<24) | bit_blows;
  829.  
  830.    if (ALIVE(defender)) {   // defeat first; its easier
  831.       if (attacker->ship) {  // he could be attacking from on board a ship!
  832.          attacker->ship->cargo--;
  833.          attacker->ship->weight -= cargo_weight(attacker->type);
  834.       FI
  835.       Remove((struct Node *)attacker);
  836.       // increment Units Lost in Combat
  837.       roster[attacker->owner].ulc[attacker->type]++;
  838.       roster[defender->owner].eud[attacker->type]++;
  839.       control_flag = UNIT_LOST;
  840.       /* record the event for posterity */
  841.       battle.winner = battle.def_owner;
  842.    } else {    // victory here
  843.       // go down the list for defending units to destroy
  844.       // we must do multiple searches, because destroying a unit messes
  845.       // up the list structure
  846.       BOOL unit_found;
  847.       struct Unit *unit;
  848.       do {
  849.          unit_found = FALSE;
  850.          for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  851.             if (unit->col==targx && unit->row==targy && unit->owner!=attacker->owner) {
  852.                if (attacker->type==FIGHTER && bombardment==TRUE)
  853.                   if (wishbook[unit->type].range<=0)  // not an aircraft
  854.                      continue;
  855.                Remove((struct Node *)unit);
  856.                roster[unit->owner].ulc[unit->type]++;    // inc. Units Lost in Combat
  857.                roster[attacker->owner].eud[unit->type]++;   // inc. Enemy Units Destroyed
  858.                battle.casualties++;
  859.                destruct_unit(unit);
  860.                unit_found = TRUE;
  861.                break;
  862.             FI
  863.       } while (unit_found);
  864.       // if theres a city here, attacker conquers it
  865.       captured = city_hereP(targx,targy);
  866.       if (captured)
  867.          if (captured->owner)    // a non-neutral city is taken
  868.             battle.seen_by |= mask(captured->owner);
  869.       if (captured!=NULL && bombardment==FALSE) {
  870.          // attacking unit is dispersed to hold the city
  871.          Remove((struct Node *)attacker);
  872.          roster[attacker->owner].ulc[attacker->type]++;   // inc. Units Lost in Combat
  873.          control_flag = UNIT_LOST;
  874.       FI
  875.       battle.winner = battle.att_owner;
  876.    FI
  877.  
  878.    // display the battle
  879.    if (PLAYER.show & SHOW_GRP)  show_battle();
  880.    if (captured!=NULL && bombardment==FALSE)
  881.       conquer_city(captured);
  882.       // BSH 1/10 - Removed the check for human player -
  883.       //        always call conquer_city - it checks inside
  884.       //        for human player and calls AI when needed
  885.  
  886.    // clean up some things left over after movement & battle
  887.    if (control_flag!=UNIT_LOST)
  888.       if (wishbook[attacker->type].range>0 && attacker->fuel<=0) {
  889.          // aircraft out of fuel, crashes
  890.          Remove((struct Node *)attacker);
  891.          roster[attacker->owner].ulc[attacker->type]++;
  892.          control_flag = UNIT_LOST;
  893.       FI
  894.    if (control_flag==UNIT_LOST)
  895.       destruct_unit(attacker);   // destruct and free RAM
  896.    {
  897.       BOOL vis=Bool(roster[att_player].show&SHOW_GRP);
  898.       explore_at_hex(att_player,targx,targy,vis,TRUE);
  899.    }
  900.    if (def_player>0)    // neutral cities never explore
  901.       explore_at_hex(def_player,targx,targy,INVISIBLE,TRUE);
  902.  
  903.    if (control_flag!=UNIT_LOST)
  904.       if (roster[attacker->owner].show & SHOW_GRP)
  905.          unit_status_bar(attacker);
  906.  
  907.    {  // write the combat report file
  908.       BPTR outfile;
  909.  
  910.       strcpy(foo,prefix);
  911.       strcat(foo,id_filetag);
  912.       strcat(foo,".CR");
  913.       if (outfile = Open(foo,MODE_OLDFILE)) {
  914.          Seek(outfile,0L,OFFSET_END);
  915.          Write(outfile,&battle,sizeof(battle));
  916.          Close(outfile);
  917.       FI
  918.    }
  919. }
  920.  
  921.  
  922. /*
  923.    This is part of the NCS (New Combat System).
  924.  
  925.    This NCS function will accept the information about a combat event and
  926.    calculate the attack and defense modifiers for both units -- taking into
  927.    account unit types, terrain, stacking, and the global handicap values of
  928.    the two players.
  929. */
  930.  
  931. void NCS_combat_mods(attacker, defender, att_mod, def_mod)
  932. struct Unit *attacker, *defender;
  933. int *att_mod, *def_mod;
  934. {
  935.    int terrain = get(t_grid,defender->col,defender->row);
  936.    int atype = attacker->type;
  937.    int dtype = defender->type;
  938.  
  939.    // I can do this shortcut because airfields defend like infantry in
  940.    // every respect; of course it is only changed locally in this function!
  941.    if (dtype==AIRFIELD)
  942.       dtype = RIFLE;
  943.  
  944.    // start with the global handicap values of the players
  945.    *att_mod = roster[attacker->owner].att;
  946.    *def_mod = roster[defender->owner].def;
  947.  
  948.    // transports always have a -4 on defense
  949.    if (dtype==TRANSPORT)
  950.       *def_mod -= 4;
  951.  
  952.    // transports and carriers have a -4 on attack
  953.    if (atype==TRANSPORT || atype==CARRIER)
  954.       *att_mod -= 4;
  955.  
  956.    // carriers have a -2 defense and -1 for every 2 fighters on board
  957.    if (dtype==CARRIER) {
  958.       *def_mod -= 2;
  959.       {      // count units on board
  960.          int ctr = 0;
  961.          struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  962.          for (; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  963.             if (unit->ship==defender) ctr++;
  964.          *def_mod -= ctr/2;
  965.       }
  966.    }
  967.  
  968.    // submarines have a basic -3 defense
  969.    if (dtype==SUB)
  970.       *def_mod -= 3;
  971.  
  972.    // fighters & bombers have a +3 defending against ships
  973.    if (dtype==FIGHTER || dtype==BOMBER)
  974.       if (wishbook[atype].ship_flag)
  975.          *def_mod += 3;
  976.  
  977.    // fighters have a +3 attack against bombers or aircav
  978.    if (dtype==BOMBER || dtype==AIRCAV)
  979.       if (atype==FIGHTER)
  980.          *att_mod += 3;
  981.  
  982.    /*
  983.       ground units defending:
  984.          -3 against naval bombardment
  985.          -2 against bombers
  986.          -1 against other aircraft
  987.    */
  988.    if (dtype==RIFLE || dtype==ARMOR) {
  989.       switch (dtype) {
  990.          case CRUISER:
  991.          case BATTLESHIP:
  992.             *def_mod--;
  993.          case BOMBER:
  994.             *def_mod--;
  995.          case FIGHTER:
  996.          case AIRCAV:
  997.             *def_mod--;
  998.       }
  999.    }
  1000.  
  1001.    /* That takes care of the unit-based modifiers.  Now the terrain stuff! */
  1002.    switch (terrain) {
  1003.  
  1004.       case HEX_DESERT:
  1005.          // ground forces -2 defense against aircraft
  1006.          if (dtype==RIFLE || dtype==ARMOR)
  1007.             if (wishbook[atype].range>0)
  1008.                *def_mod -= 2;
  1009.          // infantry -2 defense against armor
  1010.          if (dtype==RIFLE && atype==ARMOR)
  1011.             *def_mod -= 2;
  1012.          break;
  1013.  
  1014.       case HEX_BRUSH:
  1015.          // ground forces +1 defense against fighters or bombers
  1016.          if (dtype==RIFLE || dtype==ARMOR)
  1017.             if (atype==FIGHTER || atype==BOMBER)
  1018.                *def_mod++;
  1019.          break;
  1020.  
  1021.       case HEX_FOREST:
  1022.          // ground forces +2 defense against fighters or bombers
  1023.          if (dtype==RIFLE || dtype==ARMOR)
  1024.             if (atype==FIGHTER || atype==BOMBER)
  1025.                *def_mod += 2;
  1026.          // infantry +2 defense against armor
  1027.          if (dtype==RIFLE && atype==ARMOR)
  1028.             *def_mod += 2;
  1029.          // armor -2 defense against infantry
  1030.          if (dtype==ARMOR && atype==RIFLE)
  1031.             *def_mod -= 2;
  1032.          break;
  1033.  
  1034.       case HEX_JUNGLE:
  1035.          // ground forces +3 defense against fighters or bombers
  1036.          if (dtype==RIFLE || dtype==ARMOR)
  1037.             if (atype==FIGHTER || atype==BOMBER)
  1038.                *def_mod += 3;
  1039.          // infantry +3 defense against armor
  1040.          if (dtype==RIFLE && atype==ARMOR)
  1041.             *def_mod += 3;
  1042.          // armor -3 defense against infantry
  1043.          if (dtype==ARMOR && atype==RIFLE)
  1044.             *def_mod -= 3;
  1045.          break;
  1046.  
  1047.       case HEX_SWAMP:
  1048.          // ground forces +1 defense against aircraft
  1049.          if (dtype==RIFLE || dtype==ARMOR)
  1050.             if (wishbook[atype].range>0)
  1051.                *def_mod++;
  1052.          // infantry +2 defense against armor shelling
  1053.          if (dtype==RIFLE && atype==ARMOR)
  1054.             *def_mod += 2;
  1055.          break;
  1056.  
  1057.       case HEX_RUGGED:
  1058.          // ground forces, +1 defending against fighter or bomber
  1059.          if (dtype==RIFLE || dtype==ARMOR)
  1060.             if (atype==FIGHTER || atype==BOMBER)
  1061.                *def_mod++;
  1062.          break;
  1063.  
  1064.       case HEX_HILLS:
  1065.          // ground forces, +2 defending against fighter or bomber
  1066.          // ground forces, +1 defending against cruiser or battleship
  1067.          if (dtype==RIFLE || dtype==ARMOR) {
  1068.             if (atype==FIGHTER || atype==BOMBER)
  1069.                *def_mod += 2;
  1070.             if (atype==CRUISER || atype==BATTLESHIP)
  1071.                *def_mod += 1;
  1072.          }
  1073.          break;
  1074.  
  1075.       case HEX_MOUNTAINS:
  1076.          // ground forces, +3 defending against fighter or bomber
  1077.          // ground forces, +2 defending against air cavalry
  1078.          // ground forces, +2 defending against cruiser or battleship
  1079.          if (dtype==RIFLE || dtype==ARMOR) {
  1080.             if (atype==FIGHTER || atype==BOMBER)
  1081.                *def_mod += 3;
  1082.             if (atype==AIRCAV)
  1083.                *def_mod += 2;
  1084.             if (atype==CRUISER || atype==BATTLESHIP)
  1085.                *def_mod += 2;
  1086.          }
  1087.          // infantry defending +3 against armor shelling
  1088.          if (dtype==RIFLE && atype==ARMOR)
  1089.             *def_mod += 3;
  1090.          break;
  1091.  
  1092.       case HEX_SHALLOWS:
  1093.          // subs attack -3 against ships
  1094.          if (atype==SUB && wishbook[dtype].ship_flag)
  1095.             *att_mod -= 3;
  1096.          // subs defend -2 (on top of the -3 they already have!)
  1097.          if (dtype==SUB)
  1098.             *def_mod -= 2;
  1099.          // all other ships defend at -1 in shallows
  1100.          if (wishbook[dtype].ship_flag && dtype!=SUB)
  1101.             *def_mod--;
  1102.          break;
  1103.  
  1104.       case HEX_DEPTH:
  1105.          // sub defends at +3 in depths
  1106.          if (dtype==SUB)
  1107.             *def_mod += 3;
  1108.          break;
  1109.    }
  1110. }
  1111.  
  1112.  
  1113. /* This routine determines the basic attack values.  I'm just passing in
  1114.    values instead of unit pointers so I can use this later for the AI
  1115.    player(s) to do "what if" scenarios and determine what is risky/not
  1116.    risky attack.
  1117.    */
  1118. void   get_attack_values ( short att_type, int att_terrain, int att_player,
  1119.        short def_type, int def_terrain, int def_player,
  1120.        int* attfact, int* deffact)
  1121. {
  1122.     /*was attfact = 100; */
  1123.    // Now we use the attack factor from the start game setting for the player
  1124.    *attfact = roster[att_player].att;
  1125.    if (att_type==TRANSPORT || att_type==CARRIER)
  1126.       *attfact /= 2;
  1127.  
  1128.    /*was deffact = 100; */
  1129.    // Now we use the defense factor from the start game settings
  1130.    *deffact = roster[def_player].def;
  1131.    if (def_type==TRANSPORT || def_type==SUB)
  1132.       *deffact *= 6/10;
  1133.    if (def_type==RIFLE && (att_type==CRUISER || att_type==BATTLESHIP))
  1134.       *deffact *= 6/10;
  1135.    if (def_type==BOMBER && att_type==FIGHTER)
  1136.       *deffact *= 6/10;
  1137.  
  1138.    // terrain effects modify attack and defense factors
  1139.    switch (att_type) {
  1140.       case ARMOR:
  1141.       case BOMBER:
  1142.          if (def_terrain==HEX_DESERT)
  1143.             *attfact += 25;
  1144.          break;
  1145.       case AIRCAV:
  1146.          if (def_terrain==HEX_FOREST)
  1147.             *attfact += 10;
  1148.          if (def_terrain==HEX_JUNGLE)
  1149.             *attfact += 20;
  1150.          break;
  1151.    }
  1152.    if (def_type==RIFLE) {
  1153.       if (def_terrain==HEX_FOREST)
  1154.          *deffact += 10;
  1155.       if (def_terrain==HEX_JUNGLE)
  1156.          *deffact += 20;
  1157.    }
  1158.  
  1159.    // altitude affects odds between ground units (never ships or planes)
  1160.    if (att_type==RIFLE || att_type==ARMOR)
  1161.       if (def_type==RIFLE || def_type==ARMOR) {
  1162.          switch (att_terrain) {
  1163.             case HEX_MOUNTAINS:   // these are cumulative
  1164.                *attfact += 10;    // neat, huh?
  1165.             case HEX_HILLS:
  1166.                *attfact += 10;
  1167.             case HEX_RUGGED:
  1168.                *attfact += 10;
  1169.          }
  1170.          switch (def_terrain) {
  1171.             case HEX_MOUNTAINS:
  1172.                *deffact += 10;
  1173.             case HEX_HILLS:
  1174.                *deffact += 10;
  1175.             case HEX_RUGGED:
  1176.                *deffact += 10;
  1177.          }
  1178.       FI
  1179.    /* End if */
  1180. }
  1181.  
  1182.  
  1183. // this is where each player starts the game; it selects the home city, sets
  1184. // initial production, etc.
  1185. //  Broken up into two routines so that the player will not be surprised
  1186. void create_initial_city()
  1187. {  // put a player on the map!
  1188.  
  1189.    struct City *home_city;    /* this player's starting city */
  1190.  
  1191.    // STEP ONE: locate a home city for him
  1192.    do {
  1193.       struct City *metro = (struct City *)city_list.mlh_Head;
  1194.       long num_cities = count_nodes(&city_list);
  1195.       int home_city_index = RangeRand(num_cities)+1;
  1196.       int ctr;
  1197.  
  1198.       for ( ctr = 1; metro->cnode.mln_Succ; metro = (struct City *)metro->cnode.mln_Succ, ctr++)
  1199.          if (ctr == home_city_index) {
  1200.             home_city = metro;
  1201.             break;
  1202.          FI
  1203.    } while (home_city->owner);  /* make sure it's a neutral city */
  1204.  
  1205.    // Make him the owner
  1206.    home_city->owner = player;
  1207.  
  1208.    return;
  1209. }
  1210. void jumpstart_player()
  1211. {
  1212.     /* Find the player's home city */
  1213.    struct City* metro = (struct City*)city_list.mlh_Head;
  1214.    while( (metro->cnode.mln_Succ) && (metro->owner != player) )
  1215.       metro = (struct City*)metro->cnode.mln_Succ;
  1216.  
  1217.    // create his map display, all spaces unknown
  1218.    if (alloc_map(&PLAYER.map)==FALSE)
  1219.       clean_exit(2,"ERROR: Fatal RAM allocation disaster!\n");
  1220.  
  1221.    // put his city and surrounding area on the map
  1222.    explore_at_hex(player,metro->col,metro->row,INVISIBLE,TRUE);
  1223.  
  1224.    // If File-Mail play create passwords
  1225.    //if(fmail)
  1226.    //     create_passlock();
  1227.         
  1228.    // call him to the console and show the map
  1229.    create_player_display(metro->col,metro->row);
  1230.  
  1231.    // initialize his city production
  1232.    conquer_city(metro);
  1233. }
  1234.  
  1235.  
  1236. void unit_name_request(metro,unit)
  1237. struct City *metro;
  1238. struct Unit *unit;
  1239. {
  1240.    BOOL exam=FALSE, ship=wishbook[unit->type].ship_flag;
  1241.    char *name;
  1242.    struct Window *name_window=NULL;
  1243.    struct Gadget *context, *name_gad, *cont_gad, *prod_gad;
  1244.    struct NewGadget button = {
  1245.       77,66,      // leftedge, topedge
  1246.       98,16,   // width, height
  1247.       "_Continue",  // text label
  1248.       NULL,    // font
  1249.       1,       // gadget ID
  1250.       NULL,NULL,NULL
  1251.    }, strfield = {
  1252.       173,40,      // leftedge, topedge
  1253.       200,16,   // width, height
  1254.       "Ship _Name:", // text label
  1255.       NULL,    // font
  1256.       3,       // gadget ID
  1257.       PLACETEXT_LEFT,
  1258.       NULL,NULL
  1259.    };
  1260.  
  1261.    /* make sure user doesn't play with the map window now */
  1262.    SetPointer(map_window,BUSY_POINTER);
  1263.    ModifyIDCMP(map_window,NULL);
  1264.  
  1265.    // create the [Continue] and [Production] buttons
  1266.    if (!CreateContext(&context))
  1267.       clean_exit(1,"Unable to create context gadget!");
  1268.    button.ng_VisualInfo = vi;
  1269.    button.ng_TextAttr = &topaz11bold;
  1270.    cont_gad = CreateGadget(BUTTON_KIND,context,&button,
  1271.       GT_Underscore, '_',
  1272.       TAG_END);
  1273.    button.ng_LeftEdge = 221;
  1274.    button.ng_GadgetText = "_Production";
  1275.    button.ng_TextAttr = &topaz11;
  1276.    button.ng_Flags = NULL;
  1277.    prod_gad = CreateGadget(BUTTON_KIND,cont_gad,&button,
  1278.       GT_Underscore, '_',
  1279.       TAG_END);
  1280.  
  1281.    // randomly select a name for this unit
  1282.    name = random_name(unit->type);
  1283.  
  1284.    // create the name field
  1285.    strfield.ng_VisualInfo = vi;
  1286.    strfield.ng_TextAttr = &topaz11;
  1287.    if (ship)
  1288.    name_gad = CreateGadget(STRING_KIND,prod_gad,&strfield,
  1289.       GTST_String,            name,
  1290.       GTST_MaxChars,          19L,
  1291.       STRINGA_Justification,  GACT_STRINGCENTER,
  1292.       GT_Underscore,          '_',
  1293.       TAG_END);
  1294.  
  1295.    // do the window itself
  1296.    {
  1297.       int x, y, wd=414, ht=88;
  1298.  
  1299.       // I need to do some pixelly math to figure out the most
  1300.       // aesthetically pleasing place to put the requester
  1301.       // It must be near the city in question, yet not hide it from view.
  1302.       log_to_abs(metro->col,metro->row,&x,&y);
  1303.       x -= 30;
  1304.       y += 45;
  1305.       if (y>map_window->Height-ht)
  1306.          y -= (90+ht);
  1307.  
  1308.       name_window = OpenWindowTags(NULL,
  1309.          WA_Gadgets,       context,
  1310.          WA_Title,         "New unit produced...",
  1311.          WA_CustomScreen,  map_screen,
  1312.          WA_Top,y,         WA_Left,x,
  1313.          WA_Height,ht,     WA_Width,wd,
  1314.          WA_IDCMP,         IDCMP_GADGETUP|IDCMP_VANILLAKEY,
  1315.          WA_Flags,         NOCAREREFRESH|WFLG_DRAGBAR|WFLG_ACTIVATE,
  1316.          TAG_END );
  1317.    }
  1318.    if (name_window==NULL)
  1319.       clean_exit(1,"ERROR: Unable to open production requester!");
  1320.  
  1321.    // render in some text and a little picture
  1322.    rast_port = name_window->RPort;
  1323.    sprintf(foo,"%s produced a %s.",metro->name,wishbook[unit->type].name);
  1324.    plot_text(10,21,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1325.    px_plot_icon(unit->type,45,39,PLAYER.color,0,FALSE);
  1326.    if (!ship) {
  1327.       sprintf(foo,"%s %s",name,wishbook[unit->type].name);
  1328.       plot_text(125,43,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1329.    FI
  1330.  
  1331.    {  // handle the user actions here
  1332.       struct IntuiMessage *message; // the message the IDCMP sends us
  1333.  
  1334.       // useful for interpreting IDCMP messages
  1335.       UWORD code;
  1336.       ULONG class;
  1337.       APTR object;
  1338.       UWORD qualifier;
  1339.  
  1340.       FOREVER {
  1341.          WaitPort(name_window->UserPort);
  1342.          while (message = GT_GetIMsg(name_window->UserPort)) {
  1343.             code = message->Code;  // MENUNUM
  1344.             object = message->IAddress;  // Gadget
  1345.             class = message->Class;
  1346.             qualifier = message->Qualifier;
  1347.             GT_ReplyIMsg(message);
  1348.             if (class==IDCMP_VANILLAKEY) {
  1349.                switch ((char)code) {
  1350.                   case 13:
  1351.                   case 'c':    // default for CONTINUE
  1352.                      // show the button depressed
  1353.                      show_depress(cont_gad,name_window->RPort);
  1354.                      Delay(10L);
  1355.                      goto exit_name_window;
  1356.                   case 'p':   // default for PRODUCTION
  1357.                      show_depress(prod_gad,name_window->RPort);
  1358.                      Delay(10L);
  1359.                      exam = TRUE;
  1360.                      goto exit_name_window;
  1361.                   case 'n':   // activate the NAME string
  1362.                      ActivateGadget(name_gad,name_window,NULL);
  1363.                }
  1364.             FI
  1365.             if (class==IDCMP_GADGETUP) {
  1366.                if (object==cont_gad) {
  1367.                   goto exit_name_window;
  1368.                FI
  1369.                if (object==prod_gad) {
  1370.                   exam = TRUE;
  1371.                   goto exit_name_window;
  1372.                FI
  1373.             FI
  1374.          OD
  1375.       OD
  1376.    }
  1377.    exit_name_window:
  1378.  
  1379.    // fetch new name string from the gadget
  1380.    if (ship)
  1381.       name_unit(unit,((struct StringInfo *)name_gad->SpecialInfo)->Buffer);
  1382.    else
  1383.       name_unit(unit,name);
  1384.  
  1385.    // now close up everything with the mapsize_window
  1386.    CloseWindow(name_window);
  1387.    rast_port = map_window->RPort;
  1388.    name_window = NULL;
  1389.    FreeGadgets(context);
  1390.    ClearPointer(map_window);
  1391.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  1392.    if (exam)
  1393.       examine_city(metro);
  1394. }
  1395.  
  1396.  
  1397. // go through the production of all cities owned by the current player, and
  1398. // do this turn's production of military units, etc.
  1399. // it returns the number of units produced
  1400.  
  1401. int do_cities_production()
  1402. {
  1403.    struct City *metro = (struct City *)city_list.mlh_Head;
  1404.    int ctr = 0;
  1405.  
  1406.    /*
  1407.         The map wasn't updating correctly sometimes, so I decided to make
  1408.         two passes on the city list.  Now it goes through the list once to
  1409.         update the WIP and the map area around the cities, then a second
  1410.         pass for actual production of units.
  1411.    */
  1412.  
  1413.    for( ; metro->cnode.mln_Succ; metro=(struct City*)metro->cnode.mln_Succ)
  1414.       if( metro->owner == player) {
  1415.          metro->unit_wip += (metro->industry*PLAYER.prod)/50;
  1416.          explore_at_hex(player,metro->col,metro->row,INVISIBLE,TRUE);
  1417.       }
  1418.  
  1419.    metro = (struct City*)city_list.mlh_Head;
  1420.    for ( ; metro->cnode.mln_Succ; metro = (struct City *)metro->cnode.mln_Succ)
  1421.       if (metro->owner==player) {
  1422.         if (metro->unit_wip >= wishbook[metro->unit_type].build) {
  1423.             struct Unit *new_unit = AllocVec((int)sizeof(*new_unit),MEMF_CLEAR);
  1424.  
  1425.             metro->unit_wip = 0;
  1426.  
  1427.             // build the unit
  1428.             new_unit->col = metro->col;
  1429.             new_unit->row = metro->row;
  1430.             new_unit->owner = player;
  1431.             new_unit->type = metro->unit_type;
  1432.             new_unit->move = unit_speed(new_unit);
  1433.             new_unit->damage = 0;
  1434.  
  1435.             // Added by BSH
  1436.             new_unit->attacks = 0;
  1437.             new_unit->cargo = 0;
  1438.             new_unit->ship = NULL;
  1439.             new_unit->orders = NULL;
  1440.             // End Added BSH
  1441.  
  1442.             new_unit->fuel = wishbook[new_unit->type].range;
  1443.             name_unit(new_unit,"UNNAMED");
  1444.  
  1445.             AddTail((struct List *)&unit_list,(struct Node *)new_unit);
  1446.             ctr++;
  1447.  
  1448.             // now inform the user and let him reset the production
  1449.             if (wishbook[new_unit->type].ship_flag || PLAYER.show_production) {
  1450.                if (display==FALSE)
  1451.                   create_player_display(metro->col,metro->row);
  1452.                if (need_to_scrollP(metro->col,metro->row)) {
  1453.                   int ox=xoffs, oy=yoffs;
  1454.  
  1455.                   set_display_offsets(metro->col,metro->row);
  1456.                   GP_smart_scroll(ox,oy);
  1457.                FI
  1458.  
  1459.                // do this so the user can see which city he is looking at
  1460.                save_hex_graphics(metro->col,metro->row,0);    // blit the background to a safe place
  1461.                plot_mapobject(metro->col,metro->row,MAP_MARKER);
  1462.                unit_name_request(metro,new_unit);
  1463.  
  1464.                // unmark the city on screen
  1465.                GP_update_hex_display(metro->col,metro->row);
  1466.             FI
  1467.          FI
  1468.       FI
  1469.    return ctr;
  1470. }
  1471.  
  1472.  
  1473. void unit_status_bar(unit)
  1474. struct Unit *unit;
  1475. {
  1476.     char foo2[30];
  1477.    /*
  1478.       With ships, the unit type goes first, like for example...
  1479.          Battleship Texas
  1480.       ...but with other units the name goes first, then the type...
  1481.          101st Infantry
  1482.    */
  1483.    if (wishbook[unit->type].ship_flag)
  1484.       sprintf(foo,"%s %s",wishbook[unit->type].name,unit->name);
  1485.    else
  1486.       sprintf(foo,"%s %s",unit->name,wishbook[unit->type].name);
  1487.    if (wishbook[unit->type].hitpoints>1) {
  1488.       sprintf(bar,"  hits:%ld/%ld",unit->damage,wishbook[unit->type].hitpoints);
  1489.       strcat(foo,bar);
  1490.    FI
  1491.    if (wishbook[unit->type].range>0) {
  1492.       sprintf(bar,"  fuel:%ld/%ld",unit->fuel,wishbook[unit->type].range);
  1493.       strcat(foo,bar);
  1494.    FI
  1495.    if (unit->type==TRANSPORT || unit->type==CARRIER) {
  1496.       sprintf(bar,"  cargo:%ld/%ld",unit->cargo,(unit->type==TRANSPORT)?6:8);
  1497.       strcat(foo,bar);
  1498.    FI
  1499.    if (unit->orders)
  1500.       switch (unit->orders->type) {
  1501.          case ORDER_LOAD:
  1502.             strcat(foo,"   orders: Load Ship");
  1503.             break;
  1504.          case ORDER_SENTRY:
  1505.             strcat(foo,"   orders: Sentry Duty");
  1506.             break;
  1507.          case ORDER_FORTIFY:
  1508.             strcat(foo,"   orders: Dig In");
  1509.             break;
  1510.          case ORDER_UNLOAD:
  1511.            strcat(foo,"   status: Unloading Units");
  1512.            break;
  1513.          case ORDER_FORTIFIED:
  1514.            strcat(foo,"   status: In Fortified Position");
  1515.            break;
  1516.          case ORDER_GOTO:
  1517.            if( !unit->orders->dest_unit ) {
  1518.              if( unit->orders->etc >= 0 ) {
  1519.                 strcat(foo,"   orders: Goto Hex: ");
  1520.                 sprintf(foo2, "%ld, %ld", unit->orders->destx,
  1521.                      unit->orders->desty);
  1522.                 strcat(foo, foo2);
  1523.              }
  1524.              else {
  1525.                 strcat(foo,"   orders: Patrol Between: ");
  1526.                 sprintf(foo2, "%ld, %ld and %ld, %ld",
  1527.                     unit->orders->destx, unit->orders->desty,
  1528.                     unit->orders->orgx, unit->orders->orgy);
  1529.                 strcat(foo, foo2);
  1530.              }
  1531.            }
  1532.            else {
  1533.              strcat(foo,"   orders: Rendevous with Ship: ");
  1534.              if( AI3_AssertUnit( unit->orders->dest_unit ) )
  1535.                 strcat(foo, unit->orders->dest_unit->name);
  1536.            }
  1537.            break;
  1538.       } // end switch
  1539.  
  1540.    strncpy(win_title,foo,79L);
  1541.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1542.  
  1543.    // now do the unit movement bar
  1544.    {
  1545.       int barleft, barright;
  1546.       int bartop, barbottom, barheight;
  1547.       int amplitude, ctr;
  1548.  
  1549.       InstallClipRegion(map_window->WLayer,bar_region);
  1550.       SetRast(rast_port,BLACK);
  1551.  
  1552.       // preliminary calculations before I do the graphics
  1553.       bartop = map_window->BorderTop+4;
  1554.       barbottom = map_window->Height-map_window->BorderBottom-15;
  1555.       barleft = map_window->BorderLeft+4;
  1556.       barright = barleft+5;
  1557.       barheight = barbottom-bartop+1;
  1558.       amplitude = (unit->move*barheight)/wishbook[unit->type].speed;
  1559.  
  1560.       // draw the green color bar
  1561.       SetAPen(rast_port,GREEN);
  1562.       if (wishbook[unit->type].range>0) // i.e. an aircraft
  1563.          SetAPen(rast_port,LT_BLUE);
  1564.       if (wishbook[unit->type].ship_flag)
  1565.          SetAPen(rast_port,DK_BLUE);
  1566.  
  1567.       RectFill(rast_port,barleft,barbottom-amplitude,barright,barbottom);
  1568.  
  1569.       // draw the graduation markers
  1570.       SetAPen(rast_port,WHITE);
  1571.       for (ctr=0; ctr<=wishbook[unit->type].speed; ctr+=60) {
  1572.             amplitude = (ctr*barheight)/wishbook[unit->type].speed;
  1573.             Move(rast_port,barleft,barbottom-amplitude);
  1574.             Draw(rast_port,barright,barbottom-amplitude);
  1575.       OD
  1576.  
  1577.       InstallClipRegion(map_window->WLayer,map_region);
  1578.    }
  1579. }
  1580.  
  1581.  
  1582. void hex_status_bar(col,row)
  1583. int col, row;
  1584. {
  1585.    struct Unit *unit;
  1586.    struct MapIcon *icon;
  1587.    struct City *metro;
  1588.    // static char *terrain[]={
  1589.    //   "Unexplored Area",
  1590.    //   "Forbidden",
  1591.    //   "Plains",
  1592.    //   "Desert",
  1593.    //   "Scrubland",
  1594.    //   "Forest",
  1595.    //   "Jungle",
  1596.    //   "Rough Country",
  1597.    //   "Hills",
  1598.    //   "Mountains",
  1599.    //   "Mountain Peaks",
  1600.    //   "Swamp",
  1601.    //   "Shallow Waters",
  1602.    //   "Ocean",
  1603.    //   "Deep Ocean",
  1604.    //   "Pack Ice"
  1605.    //};
  1606.  
  1607.    clear_movebar();
  1608.    // look for a city
  1609.    if (metro=city_hereP(col,row)) {
  1610.       strncpy(win_title,metro->name,79L);
  1611.       SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1612.       return;
  1613.    FI
  1614.    // look for FRIENDLY units (units on ships dont count)
  1615.    for (unit = (struct Unit *)unit_list.mlh_Head; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  1616.       if (unit->col==col && unit->row==row)
  1617.          if (unit->owner==player && unit->ship==NULL) {
  1618.             // unit belongs to me; let unit_status_bar() handle it
  1619.             unit_status_bar(unit);
  1620.             return;
  1621.          FI
  1622.    // look for HOSTILE units
  1623.    for (icon=(struct MapIcon *)PLAYER.icons.mlh_Head;icon->inode.mln_Succ;icon=(struct MapIcon *)icon->inode.mln_Succ)
  1624.       if (icon->col==col && icon->row==row && icon->owner!=player) {
  1625.          sprintf(foo,"HOSTILE: %s's %s",roster[icon->owner].name,wishbook[icon->type].name);
  1626.          strncpy(win_title,foo,79L);
  1627.          SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1628.          return;
  1629.       FI
  1630.  
  1631.    strncpy(win_title,terrain_name_table[get(PLAYER.map,col,row)],79L);
  1632.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1633. }
  1634.  
  1635.  
  1636. /*
  1637.    unit "cargo" is attempting to load onto a transport or carrier at this
  1638.    location; note that there may be multiple ships in this hex; we must
  1639.    try to find one that can accept the unit
  1640.    The return value is TRUE if a suitable ship was found.  This does not
  1641.    mean the ship was actually boarded, because it may have been full.
  1642.    The calling function should check the cargo->ship value to find out.
  1643. */
  1644.  
  1645. BOOL board_ship(cargo,col,row)
  1646. struct Unit *cargo;
  1647. int col, row;
  1648. {
  1649.    struct Unit *ship=NULL;
  1650.    int shiptype=NULL;
  1651.    BOOL result = FALSE;
  1652.    int weight=cargo_weight(cargo->type);
  1653.  
  1654.    // determine what kind of ship we need to look for: TRANSPORT for ground
  1655.    // units, or CARRIER for aircraft
  1656.    switch (cargo->type) {
  1657.       case ARMOR:
  1658.       case RIFLE:
  1659.          shiptype=TRANSPORT;
  1660.          break;
  1661.       case FIGHTER:
  1662. //      case BOMBER:
  1663. //      case AIRCAV:
  1664.          shiptype=CARRIER;
  1665.          break;
  1666.       default:
  1667.          return result;
  1668.    };
  1669.  
  1670.    /*
  1671.       search for ships of the appropriate type in this hex
  1672.       we don't need to check who they belong to, the hex must be friendly
  1673.       or this function wouldn't have been called (I hope!)
  1674.    */
  1675.    ship=(struct Unit *)unit_list.mlh_Head;
  1676.    for (; ship->unode.mln_Succ; ship=(struct Unit *)ship->unode.mln_Succ)
  1677.       if (ship->type==shiptype && ship->col==col && ship->row==row) {
  1678.          result = TRUE;
  1679.          // see if there is room on the ship to accept this unit
  1680.          if (weight<=cargo_capacity(ship)-ship->weight) {
  1681.             ship->cargo++;     // take up a cargo slot on the ship
  1682.             ship->weight += weight;
  1683.             cargo->ship=ship;  /* set the cargo unit's new location */
  1684.             cargo->col=ship->col;   cargo->row=ship->row;
  1685.             cargo->move=0;     /* cargo unit's movement this turn is expended */
  1686.             if (shiptype==TRANSPORT)
  1687.                give_orders(cargo,ORDER_SENTRY,0,0,-1);   // auto-sentry them
  1688.             if (shiptype==CARRIER)
  1689.                cargo->fuel = wishbook[cargo->type].range;  // refuel aircraft
  1690.             control_flag=UNIT_DONE;    // so that movement_mode() will know
  1691.             // if this ship is under orders to load units and is now full,
  1692.             // then we should clear the orders for it, like so...
  1693.             if (ship->orders)
  1694.                if (ship->orders->type==ORDER_LOAD && ship->weight>=cargo_capacity(ship))
  1695.                   clear_orders(ship);
  1696.             explore_at_hex(player,col,row,VISIBLE,FALSE);   // update map
  1697.             return result;
  1698.          FI
  1699.       FI
  1700.    return result;
  1701. }
  1702.  
  1703.  
  1704. /*
  1705.    This function determines the cargo capacity of a ship.  It returns the
  1706.    maximum number of units the ship can carry in its current condition
  1707.    (taking damage into account), NOT the amount of free space on the ship.
  1708.  
  1709.    Depending on the value you want, use the formulas:
  1710.       free_space=cargo_capacity(ship)-ship->cargo;
  1711.    or
  1712.       free_space=cargo_capacity(ship)-ship->weight;  // taking heavy armor into account
  1713.  
  1714.    The function also returns a -1 if the unit specified is not a cargo vessel,
  1715.    so it can be used to quickly check that as well.
  1716. */
  1717. int cargo_capacity(ship)
  1718. struct Unit *ship;
  1719. {
  1720.    int capacity;
  1721.  
  1722.    switch (ship->type) {
  1723.       case TRANSPORT:
  1724.          capacity = 6;
  1725.          break;
  1726.       case CARRIER:
  1727.          capacity = 8;
  1728.          break;
  1729.       default:
  1730.          return -1;
  1731.    }
  1732.    capacity -= ship->damage*2;   // each hit reduces capacity by two units
  1733.    return capacity;
  1734. }
  1735.  
  1736.  
  1737. /*
  1738.    This function returns the weight (for loading/unloading purposes) of a given
  1739.    type of cargo.  It appears simple to the point of redundancy right now, but it
  1740.    could easily become more complex as the stacking & transporting rules are
  1741.    tinkered with.
  1742. */
  1743.  
  1744. int cargo_weight(type)
  1745. int type;
  1746. {
  1747.    int weight;
  1748.  
  1749.    switch (type) {
  1750.       case RIFLE:
  1751.       case FIGHTER:
  1752.          weight = 1;
  1753.          break;
  1754.       case ARMOR:
  1755.          weight = 2;
  1756.          break;
  1757.       default:
  1758.          weight = 100;  // an impossibly high value
  1759.    }
  1760.    return weight;
  1761. }
  1762.  
  1763.  
  1764. /*
  1765.    When a ship has been given ORDER_LOAD, it should be referred here.  This
  1766.    function will search for loadable cargo units in the same hex (as in a
  1767.    city, for example) or in surrounding hexes and attempt to load them on
  1768.    the ship.     When  the  ship  is  filled,  load_ship()  will  clear  its
  1769.    orders.  Until that happens, however, load_ship() should be called every
  1770.    turn for it.
  1771. */
  1772.  
  1773. void load_ship(ship)
  1774. struct Unit *ship;
  1775. {
  1776.    struct Unit *cargo;
  1777.    int numhexes = adjacent(ship->col,ship->row);
  1778.    int index, access;
  1779.    int units_loaded=0, weight;
  1780.    BOOL valid_cargo;
  1781.  
  1782.    if (cargo_capacity(ship)<0)
  1783.       return;    // not a cargo vessel
  1784.    if (ship->cargo>=cargo_capacity(ship) || ship->weight>=cargo_capacity(ship)) {
  1785.       clear_orders(ship);  // ship already full; clear orders
  1786.       return;
  1787.    }
  1788.  
  1789.    // this gives me an index of all hexes I can load units from
  1790.    hexlist[numhexes++].col = ship->col;
  1791.    hexlist[numhexes++].row = ship->row;
  1792.    cargo = (struct Unit *)unit_list.mlh_Head;
  1793.    for (;cargo->unode.mln_Succ;cargo=(struct Unit *)cargo->unode.mln_Succ)
  1794.       if (unit_readiness(cargo)&UNIT_READY) {
  1795.          // search to see if its in an accessible hex
  1796.          access = FALSE;
  1797.          for (index=numhexes;index>=0;index--)
  1798.             if (cargo->col==hexlist[index].col && cargo->row==hexlist[index].row)
  1799.                access = TRUE;
  1800.          if (access==FALSE)
  1801.             continue;   // too far away; try next unit
  1802.          if (cargo->ship)
  1803.             continue;   // unit is already on a ship!
  1804.          // try to determine whether this is the right kind of cargo
  1805.          if (ship->type==TRANSPORT)
  1806.             valid_cargo = (cargo->type==RIFLE || cargo->type==ARMOR);
  1807.          else
  1808.             valid_cargo = (cargo->type==FIGHTER);  // only fighters in v0.14+
  1809. //            valid_cargo = (wishbook[cargo->type].range>0);
  1810.  
  1811.          // see if armor is too heavy
  1812.          weight = cargo_weight(cargo->type);
  1813.          if (cargo_capacity(ship)-ship->weight < weight)
  1814.             valid_cargo = FALSE;
  1815.  
  1816.          if (valid_cargo) {
  1817.             units_loaded++;
  1818.             clear_orders(cargo);
  1819.             cargo->move = 0;  // loading expends movement
  1820.             cargo->ship = ship;
  1821.             cargo->col = ship->col;
  1822.             cargo->row = ship->row;
  1823.             if (ship->type==TRANSPORT)
  1824.                give_orders(cargo,ORDER_SENTRY,0,0,-1);
  1825.             ship->cargo++;
  1826.             ship->weight += weight;
  1827.  
  1828.             if (ship->cargo>=cargo_capacity(ship) || ship->weight>=cargo_capacity(ship)) {
  1829.                clear_orders(ship);
  1830.                break;
  1831.             FI
  1832.          FI
  1833.       FI
  1834.    if (units_loaded>0) {
  1835.       if (PLAYER.show&SHOW_GRP)
  1836.          explore_at_hex(player,ship->col,ship->row,VISIBLE,FALSE);
  1837.       if (PLAYER.show&SHOW_REQ) {
  1838.          sprintf(foo,"Your %s %s has taken on board %ld units.",
  1839.             wishbook[ship->type].name, ship->name, units_loaded);
  1840.          if (ship->cargo>=cargo_capacity(ship)) {
  1841.             sprintf(bar,"\nThe ship is now fully loaded.");
  1842.             strcat(foo,bar);
  1843.          FI
  1844.          (void)rtEZRequestTags(foo,"Okay",NULL,NULL,
  1845.             RT_Window,        map_window,
  1846.             RT_ReqPos,        REQPOS_CENTERSCR,
  1847.             RT_LockWindow,    TRUE,
  1848.             RTEZ_Flags,       EZREQF_CENTERTEXT,
  1849.             TAG_DONE );
  1850.       FI
  1851.    FI
  1852. }
  1853.  
  1854.  
  1855.  
  1856. /* When a unit has been given the GOTO order it will move
  1857.    towards the hex recorded in it's orders, clear it's
  1858.    orders if it hits any impediment (move result = -1),
  1859.    check when it reaches it's destination, and turn
  1860.    around and head back if it is doing a PATROL order.
  1861.    Of course it may have died moving, so we need to do all
  1862.    checks at the beginning of the routine.
  1863.    */
  1864. void  do_goto( struct Unit* unit )
  1865. {
  1866.     int    result;
  1867.     char   foo[128];
  1868.  
  1869.     if( unit->orders->dest_unit ) {
  1870.         if( !AI3_AssertUnit( unit->orders->dest_unit ) ) {
  1871.               clear_orders( unit );
  1872.               return;
  1873.         }
  1874.         AI5_CalcPath( unit->type, unit->col, unit->row,
  1875.             unit->orders->dest_unit->col,
  1876.             unit->orders->dest_unit->row, AI5_PATH_GOOD );
  1877.         // Check our fuel supply
  1878.         if( (wishbook[unit->type].range > 0) && (unit->fuel < PathLength) ) {
  1879.           clear_orders( unit );
  1880.           return;
  1881.         }
  1882.     }
  1883.     else {
  1884.         AI5_CalcPath( unit->type, unit->col, unit->row,
  1885.             unit->orders->destx, unit->orders->desty, AI5_PATH_GOOD );
  1886.         // Check our fuel supply
  1887.         if( (wishbook[unit->type].range > 0) && (unit->fuel < PathLength) ) {
  1888.           clear_orders( unit );
  1889.           return;
  1890.         }
  1891.     }
  1892.     if( Path[0] == -1 ) {
  1893.         // No path! Or we got there already.
  1894.         clear_orders( unit );
  1895.         return;
  1896.     }
  1897.     else {
  1898.         while( (unit->move > 0) && (unit->orders) && (Path[0] != -1) ) {
  1899.             result = move_unit_dir( unit, Path[0] );
  1900.             // Check if we couldn't move, missed a chance, attacked a unit
  1901.             //   or city, etc.
  1902.             if( result <= -1 ) return;
  1903.             // Else we did move and nothing bad happened, so let's look.
  1904.             if( unit->orders ) {
  1905.                 if( !unit->orders->dest_unit ) {
  1906.                     if( (unit->col == unit->orders->destx) &&
  1907.                         (unit->row == unit->orders->desty) ) {
  1908.                         // We reached a destination
  1909.                         if( unit->orders->etc >= 0 ) {
  1910.                             // Simple GOTO
  1911.                             clear_orders( unit );
  1912.                             return;
  1913.                         }
  1914.                         else {
  1915.                             // Patrol order - swap 'em.
  1916.                             short temp = unit->orders->destx;
  1917.                             unit->orders->destx = unit->orders->orgx;
  1918.                             unit->orders->orgx = temp;
  1919.                             temp = unit->orders->desty;
  1920.                             unit->orders->desty = unit->orders->orgy;
  1921.                             unit->orders->orgy = temp;
  1922.                             //return;
  1923.                 // Calculate a new path BACK.
  1924.                 AI5_CalcPath( unit->type, unit->col, unit->row,
  1925.                       unit->orders->destx, 
  1926.                       unit->orders->desty, AI5_PATH_GOOD );
  1927.                         }
  1928.                     } // End if we arrived
  1929.                     else {
  1930.                         // Create a new path for the next loop
  1931.                         AI5_CalcPath( unit->type, unit->col, unit->row,
  1932.                             unit->orders->destx, 
  1933.                             unit->orders->desty, AI5_PATH_GOOD );
  1934.                         // Check our fuel supply
  1935.                         if( (wishbook[unit->type].range > 0) && 
  1936.                             (unit->fuel < PathLength) ) {
  1937.                             clear_orders( unit );
  1938.                             return;
  1939.                         }
  1940.                         // Check our path, see if it got longer
  1941.                         if( unit->orders->etc > 0 ) {
  1942.                             unit->orders->etc--;
  1943.                             if( PathLength - unit->orders->etc > 3 ) {
  1944.                                 // we have a major obstacle
  1945.                                 if( unit->name ) {
  1946.                                     sprintf 
  1947.                                         (foo, 
  1948.                                          "Major obstacle detected for %s %s\nmoving from %ld to %ld",
  1949.                                          UnitString[unit->type],
  1950.                                          unit->name,
  1951.                                          unit->col, unit->row,
  1952.                                          unit->orders->destx,
  1953.                                          unit->orders->desty);
  1954.                                 } // End if unit->name
  1955.                                 else {
  1956.                                     sprintf 
  1957.                                         (foo, 
  1958.                                          "Major obstacle detected for %s\nmoving from %ld to %ld",
  1959.                                          UnitString[unit->type],
  1960.                                          unit->col, unit->row,
  1961.                                          unit->orders->destx,
  1962.                                          unit->orders->desty);
  1963.                                 } // End else no unit->name
  1964.                                 if( rtEZRequestTags
  1965.                                     (foo,
  1966.                                      "Clear Orders|Continue Anyway",
  1967.                                      NULL,NULL,
  1968.                                      RT_Window,     map_window,
  1969.                                      RT_ReqPos,     REQPOS_CENTERSCR,
  1970.                                      RT_LockWindow, TRUE,
  1971.                                      RTEZ_Flags,    EZREQF_CENTERTEXT,
  1972.                                      TAG_DONE ) ) {
  1973.                                         clear_orders( unit );
  1974.                                 } // End if user wants to clear orders
  1975.                             } // End if major obstacle
  1976.                         } // End if unit->orders->etc >=0
  1977.                     } // End else we have not yet arrived
  1978.                 } // End if not a goto-unit
  1979.                 else {
  1980.                     if( AI3_AssertUnit( unit->orders->dest_unit ) ) {
  1981.                         // check for reaching our destination unit
  1982.                         if( (unit->col == unit->orders->dest_unit->col) &&
  1983.                             (unit->row == unit->orders->dest_unit->row) ) {
  1984.                             clear_orders(unit);
  1985.                             return;
  1986.                         }
  1987.                         else {
  1988.                             // Create a new path for the next loop
  1989.                             AI5_CalcPath( unit->type, unit->col, unit->row,
  1990.                                 unit->orders->dest_unit->col,
  1991.                                 unit->orders->dest_unit->row, 
  1992.                                 AI5_PATH_GOOD );
  1993.                             // Check our fuel supply
  1994.                             if( (wishbook[unit->type].range > 0) && 
  1995.                                 (unit->fuel < PathLength) ) {
  1996.                                 clear_orders( unit );
  1997.                                 return;
  1998.                             }
  1999.                             // Check our path, see if it got longer
  2000.                             if( unit->orders->etc > 0 ) {
  2001.                                 unit->orders->etc--;
  2002.                                 if( PathLength - unit->orders->etc > 3 ) {
  2003.                                     // we have a major obstacle
  2004.                                     if( unit->name ) {
  2005.                                       sprintf 
  2006.                                         (foo, 
  2007.                                          "Major obstacle detected for %s %s\nmoving from %ld to %ld",
  2008.                                          UnitString[unit->type],
  2009.                                          unit->name,
  2010.                                          unit->col, unit->row,
  2011.                                          unit->orders->destx,
  2012.                                          unit->orders->desty);
  2013.                                     } // End if unit->name
  2014.                                     else {
  2015.                                       sprintf 
  2016.                                         (foo, 
  2017.                                          "Major obstacle detected for %s\nmoving from %ld to %ld",
  2018.                                          UnitString[unit->type],
  2019.                                          unit->col, unit->row,
  2020.                                          unit->orders->destx,
  2021.                                          unit->orders->desty);
  2022.                                     } // End else no unit->name
  2023.                                     if( rtEZRequestTags
  2024.                                         (foo,
  2025.                                          "Clear Orders|Continue Anyway",
  2026.                                          NULL,NULL,
  2027.                                          RT_Window,     map_window,
  2028.                                          RT_ReqPos,     REQPOS_CENTERSCR,
  2029.                                          RT_LockWindow, TRUE,
  2030.                                          RTEZ_Flags,    EZREQF_CENTERTEXT,
  2031.                                          TAG_DONE ) ) {
  2032.                                               clear_orders( unit );
  2033.                                     }
  2034.                                 } // End if major obstacle
  2035.                             } // end if unit->orders->etc >= 0
  2036.                         } // End else we did not reach our destination unit
  2037.                     }// end if we still HAVE a destination unit
  2038.                 } // end else we are trying to reach a unit not a location
  2039.             } // End if we still have orders
  2040.         }// end while
  2041.     } // end else we have a path.
  2042.     return;
  2043. }
  2044.  
  2045. // this is where a unit is moving on the map, checking all the movment
  2046. // rules, possible combat and such as we go
  2047.  
  2048. int move_unit_dir(unit,dir)
  2049. struct Unit *unit;
  2050. enum Direction dir;
  2051. {
  2052.    int targx = unit->col, targy = unit->row;
  2053.  
  2054.    switch (dir) {
  2055.       case EAST:
  2056.          targx++;
  2057.          break;
  2058.       case WEST:
  2059.          targx--;
  2060.          break;
  2061.       case NORTHEAST:
  2062.          targy--;
  2063.          if (unit->row%2) // odd number
  2064.             targx++;
  2065.          break;
  2066.       case NORTHWEST:
  2067.          targy--;
  2068.          if (!(unit->row%2)) // even number
  2069.             targx--;
  2070.          break;
  2071.       case SOUTHEAST:
  2072.          targy++;
  2073.          if (unit->row%2) // odd number
  2074.             targx++;
  2075.          break;
  2076.       case SOUTHWEST:
  2077.          targy++;
  2078.          if (!(unit->row%2)) // even number
  2079.             targx--;
  2080.          break;
  2081.       default:
  2082.          return (-8);
  2083.    }
  2084.    return (move_unit_xy(unit,targx,targy));
  2085. }
  2086.  
  2087.  
  2088. int move_unit_xy(unit,targx,targy)
  2089. struct Unit *unit;
  2090. int targx, targy;
  2091. {
  2092.         //BSH
  2093.         // This routine will now return integer values for results
  2094.         //              -6 - Attack on City (successful - but still lose unit)
  2095.         //              -5 - Attack on City (failed)
  2096.         //              -4 - Attack on enemy unit(s) (failed)
  2097.         //              -3 - Unit ran out of fuel and crashed
  2098.         //              -2 - Unit failed to move this turn (missed chance)
  2099.         //              -1 - Unit cannot move to target hex
  2100.         //              0 - Move occurred
  2101.         //              1 - Moved - Attacked enemy unit(s) (won)
  2102.         //              2 - Moved - Overflew carrier
  2103.         //              3 - Moved - Boarded ship
  2104.         //              4 - Unit moved, but unit(s) on board were lost
  2105.         // To use the unit over again, check for return > -3
  2106.  
  2107.    BOOL cargo_killed=FALSE;
  2108.    int terrain, movement_cost=(-1);
  2109.    struct City *city=city_hereP(targx,targy);  // so I don't call it over and over
  2110.  
  2111.    // correct values for wrap, if it's active
  2112.    if (wrap) {
  2113.       if (targx<0)
  2114.          targx += width;
  2115.       if (targx>=width)
  2116.          targx -= width;
  2117.       if (targy<0)
  2118.          targy += height;
  2119.       if (targy>=height)
  2120.          targy -= height;
  2121.    }
  2122.  
  2123.    /* he certainly can't move off the map! */
  2124.    //  *************  Adding disable for non-users ***************
  2125.    if (targx<0 || targx>=width || targy<0 || targy>=height) {
  2126.       if (PLAYER.show & SHOW_REQ)
  2127.          tell_user2("You cannot move off the map.",FALSE,DONK_SOUND);
  2128.       return (-1);
  2129.    FI
  2130.  
  2131.    /***
  2132.       Now I want to branch out.  I already know the user is trying to move
  2133.       a unit onto another hex.  I need to analyze the contents of that hex
  2134.       and see if he is attacking a city, attacking an enemy unit, boarding
  2135.       a ship, or just maneuvering normally.  I will branch out to various
  2136.       functions for each of these cases, except maneuvering which I can
  2137.       handle here.
  2138.    ***/
  2139.  
  2140.    // check for a destination city
  2141.    if (city)
  2142.       if (city->owner==player)  /* it's his own city */
  2143.          goto normal_movement;
  2144.    else {
  2145.       // if this unit can attack a city
  2146.       attack_hex(unit,targx,targy);  // attacking another city
  2147.       if ((unit->type == ARMOR) || (unit->type == RIFLE) || (unit->type == AIRCAV) || (unit->type == BOMBER)) {
  2148.          if (hex_owner(targx, targy)!=player)
  2149.             return -5;
  2150.          else
  2151.             return -6;
  2152.       } else
  2153.          return -1;
  2154.    }
  2155.  
  2156.    // check for hostile military units to attack
  2157.    {
  2158.       struct Unit *target = (struct Unit *)unit_list.mlh_Head;
  2159.       for (; target->unode.mln_Succ; target=(struct Unit *)target->unode.mln_Succ)
  2160.          if (target->col==targx && target->row==targy && target->owner!=player) {
  2161.             attack_hex(unit,targx,targy);
  2162.             if (hex_owner(targx, targy)!=player)
  2163.                return (-4);
  2164.             else
  2165.                return 1;
  2166.          FI
  2167.    }
  2168.  
  2169.    // check for friendly ship to board
  2170.    if (board_ship(unit,targx,targy)) {
  2171.       if (unit->ship==NULL) {    /* i.e. if unit failed to board the ship */
  2172.     /* if it's an aircraft, ask if they want to overfly the hex anyhow */
  2173.          if (wishbook[unit->type].range>0) {
  2174.             int choice;
  2175.             // If we have a human, ask him
  2176.             if (PLAYER.show & SHOW_REQ) {
  2177.                 if(PLAYER.soundfx == SOUND_ALL)
  2178.                      playSound(DONK_SOUND,PLAYER.snd_vol);
  2179.                 choice = rtEZRequestTags(\
  2180.                         "The carrier cannot accept your unit.\nDo you wish to overfly it?",
  2181.                         "Overfly|Abort", NULL, NULL,
  2182.                         RT_Window,        map_window,
  2183.                         RT_ReqPos,        REQPOS_CENTERSCR,
  2184.                         RT_LockWindow,    TRUE,
  2185.                         RT_ShareIDCMP,    TRUE,
  2186.                         RTEZ_Flags,       EZREQF_CENTERTEXT,
  2187.                         TAG_DONE );
  2188.             }
  2189.             // Otherwise, just do the overfly
  2190.                 else  choice = 1;
  2191.             if (choice==0)  return (-1);
  2192.          } else {
  2193.                 if (PLAYER.show & SHOW_REQ)
  2194.                 tell_user2("No ship in that hex can accept your troops.",FALSE,DONK_SOUND);
  2195.             return (-1);
  2196.          FI
  2197.       } else   // unit successfully boarded the ship
  2198.          return (3);
  2199.    FI
  2200.  
  2201.  normal_movement:
  2202.    // check to see if stacking rules allow him to enter this hex
  2203.    if (opt.stacking==FALSE && city==NULL) {
  2204.       BOOL valid = TRUE;
  2205.       struct Unit *target = (struct Unit *)unit_list.mlh_Head;
  2206.  
  2207.       for (; target->unode.mln_Succ; target=(struct Unit *)target->unode.mln_Succ)
  2208.          if (target->col==targx && target->row==targy) {
  2209.             int weight=cargo_weight(unit->type);
  2210.  
  2211.             valid = FALSE;
  2212.  
  2213.             if (unit->type==ARMOR || unit->type==RIFLE)
  2214.                if (target->type==TRANSPORT && target->weight<=(cargo_capacity(target)-weight)) {
  2215.                   valid = TRUE;
  2216.                   break;
  2217.                FI
  2218.             if (wishbook[unit->type].range>0)   // aircraft
  2219.                if (target->type==CARRIER && target->weight<(cargo_capacity(target)-weight)) {
  2220.                   valid = TRUE;
  2221.                   break;
  2222.                FI
  2223.          FI
  2224.       if (valid==FALSE) {   // cannot enter this hex
  2225.          if (PLAYER.show & SHOW_REQ)
  2226.                 tell_user2("Your unit cannot stack in that hex.",FALSE,DONK_SOUND);
  2227.          return (-1);
  2228.       FI
  2229.    FI
  2230.  
  2231.    terrain = get(t_grid,targx,targy);
  2232.    movement_cost = movement_cost_table[unit->type][terrain];
  2233.  
  2234.    if (city)
  2235.       movement_cost = 10;  // always easy to enter a friendly city!
  2236.  
  2237.    // is he on the road?
  2238.    if (unit->type==RIFLE || unit->type==ARMOR)
  2239.       if (get_flags(t_grid,unit->col,unit->row)&get_flags(t_grid,targx,targy)&ROAD)
  2240.          movement_cost = 30;
  2241.  
  2242.    if (terrain==HEX_FORBID) { /* Just in case a road is here! *ELS* */
  2243.       if (PLAYER.show & SHOW_REQ)
  2244.         tell_user2("Your unit cannot traverse that kind of terrain.",FALSE,DONK_SOUND);
  2245.       return (-1);
  2246.    FI
  2247.  
  2248.    if (movement_cost<0) { /* it must be a terrain this unit can't traverse */
  2249.       if (PLAYER.show & SHOW_REQ)
  2250.         tell_user2("Your unit cannot traverse that kind of terrain.",FALSE,DONK_SOUND);
  2251.       return (-1);
  2252.    FI
  2253.  
  2254.    if (city)
  2255.       movement_cost = 10;  // yes, even if there is a road here
  2256.  
  2257.    if (unit->ship)
  2258.       movement_cost = 60;  // this is always true when unloading from a ship
  2259.  
  2260.    /* if a unit's remaining movement points drop to 10 or less (which is 1/6 of
  2261.    ** a "standard" one hex move, then I round it down to zero; this is also the
  2262.    ** minimum for attacking, loading onto ships, entering cities, etc.
  2263.    */
  2264.    if (movement_cost<unit->move) {   // he definitely has enough movement for this
  2265.       unit->move -= movement_cost;
  2266.       if (unit->move<10) {
  2267.          unit->move = 0;
  2268.          control_flag = UNIT_DONE;
  2269.       FI
  2270.    } else {    // he only has enough movement for a /chance/ at this
  2271.       int chance = unit->move;
  2272.  
  2273.       unit->move = 0;
  2274.       control_flag = UNIT_DONE;
  2275.       if (RangeRand(movement_cost)>chance) {
  2276.         //BSH
  2277.          if (PLAYER.soundfx == SOUND_ALL)
  2278.                  playSound(SMASH_SOUND,PLAYER.snd_vol);
  2279.          return (-2);     // his movement attempt failed
  2280.       FI
  2281.    FI
  2282.  
  2283.    // follow him with scrolling, if necessary
  2284.    //BSH
  2285.    if (PLAYER.show & SHOW_GRP) {
  2286.       if (need_to_scrollP(targx,targy)) {
  2287.          int ox = xoffs, oy = yoffs;
  2288.  
  2289.          set_display_offsets(targx,targy);
  2290.          GP_smart_scroll(ox,oy);
  2291.       FI
  2292.       // animated movement
  2293.       anim_move(unit,unit->col,unit->row,targx,targy);
  2294.    FI
  2295.  
  2296.    // move the unit
  2297.    unit->col = targx;
  2298.    unit->row = targy;
  2299.    if (unit->ship) {     // he must be unloading from a ship
  2300.       unit->ship->cargo--;
  2301.       unit->ship->weight -= cargo_weight(unit->type);
  2302.       if (unit->ship->type==TRANSPORT) {
  2303.          unit->move = 0;   // unloading from a transport expends movement
  2304.          control_flag = UNIT_DONE;
  2305.       FI
  2306.       unit->ship=NULL;  // so we unlink him from it
  2307.    FI
  2308.    if (unit->cargo>0) {   // ship moves, cargo moves
  2309.       int ctr, capacity, num_hexes=adjacent(targx,targy);
  2310.       struct Unit *cargo;     BOOL unsentry=FALSE;
  2311.  
  2312.       /*
  2313.          If the ship has been damaged, it may no longer be able to carry all
  2314.          the troops or aircraft on board it.  If that is the case, here is
  2315.          where we kill off the extra ones, when the ship moves.  Of course,
  2316.          they can survive if the ship has just moved into a city!
  2317.       */
  2318.       capacity = 6;
  2319.       if (unit->type==CARRIER)
  2320.          capacity = 8;
  2321.       capacity -= unit->damage*2;
  2322.       /*
  2323.          kill units until capacity == cargo
  2324.          multiple searches may be required because the destruction of units
  2325.          messes up the list structure
  2326.       */
  2327.       ctr = 0;
  2328.       while (unit->cargo>capacity)
  2329.          for (cargo=(struct Unit *)unit_list.mlh_Head;cargo->unode.mln_Succ;cargo=(struct Unit *)cargo->unode.mln_Succ)
  2330.             if (cargo->ship==unit) {
  2331.                unit->cargo--;
  2332.                if (city)   // cargo is merely unloaded into the city
  2333.                   cargo->ship = NULL;
  2334.                else {      // cargo is lost at sea
  2335.                   ctr++;
  2336.                   Remove((struct Node *)cargo);
  2337.                   destruct_unit(cargo);
  2338.                   break;
  2339.                FI
  2340.             FI
  2341.       if (ctr) {
  2342.          cargo_killed = TRUE;
  2343.          if (PLAYER.show & SHOW_REQ) {
  2344.             // I must tell the user the bad news!  Number of units lost == ctr.
  2345.             sprintf(foo,"Because of battle damage, the ship is overloaded.\nYou lost %ld cargo to the sea!",ctr);
  2346.             (void)rtEZRequestTags(foo,
  2347.                "Drat!",NULL,NULL,
  2348.                RT_Window,        map_window,
  2349.                RT_ReqPos,        REQPOS_CENTERSCR,
  2350.                RT_LockWindow,    TRUE,
  2351.                RTEZ_Flags,       EZREQF_CENTERTEXT,
  2352.                TAG_DONE );
  2353.          FI
  2354.       FI
  2355.  
  2356.       /*
  2357.          Find out if there is a hostile unit or city, or neutral city
  2358.          in a hex adjacent to this one; if so, un-sentry RIFLE, ARMOR or AIRCAV.
  2359.          The "unsentry" flag will determine this.
  2360.       */
  2361.       for (ctr=0; ctr<num_hexes; ctr++) {
  2362.          int owner=hex_owner(hexlist[ctr].col,hexlist[ctr].row);
  2363.          struct City *metro=city_hereP(hexlist[ctr].col,hexlist[ctr].row);
  2364.  
  2365.          if (owner>0 && owner!=player)
  2366.             unsentry=TRUE;
  2367.          if (metro)     // check for neutral cities
  2368.             if (metro->owner!=player)
  2369.                unsentry=TRUE;
  2370.       OD
  2371.       for (cargo=(struct Unit *)unit_list.mlh_Head;cargo->unode.mln_Succ;cargo=(struct Unit *)cargo->unode.mln_Succ)
  2372.          if (cargo->ship==unit) {
  2373.             cargo->col=targx;    cargo->row=targy;
  2374.             if (unsentry)
  2375.                switch (cargo->type) {
  2376.                   case RIFLE:
  2377.                   case ARMOR:
  2378.                   case AIRCAV:
  2379.                      clear_orders(cargo);
  2380.                }
  2381.          FI
  2382.    FI
  2383.    // units entering into cities get repaired
  2384.    if (city) {
  2385.       if (unit->damage>0)
  2386.          unit->damage--;
  2387.       unit->move = 0;
  2388.       control_flag = UNIT_DONE;
  2389.    FI
  2390.    if (wishbook[unit->type].range>0) {   // units that need fuel, i.e. aircraft
  2391.       if( unit->ship == NULL ) unit->fuel--; // Added this if statement here,
  2392.       //  was just unit->fuel--, trying to fix bug where aircraft on carrier
  2393.       // lose fuel as carrier moves.
  2394.       if (city) {  // refuel
  2395.          unit->fuel = wishbook[unit->type].range;
  2396.          unit->move = 0;  /* after entering a city, the plane's turn is over */
  2397.          control_flag = UNIT_DONE;
  2398.       FI
  2399.       if (unit->fuel<=0) {
  2400.          // aircraft out of fuel, crashes!
  2401.          if (PLAYER.show & SHOW_REQ) {
  2402.             plot_mapobject(targx,targy,154,30);    // draw explosion
  2403.             context_sound(BOOM_SOUND);              // kaboomy!
  2404.             // now tell the player the awful news
  2405.             sprintf(foo,"Your %s has run out of fuel and crashed!",wishbook[unit->type].name);
  2406.             (void)rtEZRequestTags(foo,"Oh no!",NULL,NULL,
  2407.                RT_Window,     map_window,
  2408.                RT_ReqPos,     REQPOS_CENTERWIN,
  2409.                RT_LockWindow, TRUE,
  2410.                TAG_DONE);
  2411.          FI
  2412.          Remove((struct Node *)unit);           // kill the plane
  2413.          roster[unit->owner].ulc[unit->type]++;
  2414.          control_flag = UNIT_LOST;
  2415.          destruct_unit(unit);
  2416.          if (PLAYER.show & SHOW_GRP)
  2417.             explore_at_hex(player,targx,targy,VISIBLE,FALSE);
  2418.          else
  2419.             explore_at_hex(player,targx,targy,INVISIBLE,FALSE);
  2420.          return (-3);
  2421.       FI
  2422.    FI
  2423.    if (PLAYER.show & SHOW_GRP) {
  2424.       unit_status_bar(unit);
  2425.       explore_at_hex(player,targx,targy,VISIBLE,FALSE);
  2426.    } else {
  2427.       explore_at_hex(player,targx,targy,INVISIBLE,FALSE);
  2428.    FI
  2429.    if (cargo_killed==TRUE)
  2430.       return (4);
  2431.    return (0);
  2432. }
  2433.  
  2434.  
  2435. void build_move_menu()
  2436. {  // prepare the movement-mode menu for use
  2437.    struct NewMenu new_menu_strip[]  = {
  2438.       { NM_TITLE, "Project", NULL, NM_MENUDISABLED, NULL, NULL },
  2439.       { NM_ITEM, "Save Game", "S", ITEMTEXT, NULL, NULL },
  2440.       { NM_ITEM, "Save As...", NULL, ITEMTEXT, NULL, NULL },
  2441.       { NM_ITEM, NM_BARLABEL, NULL, ITEMTEXT, NULL, NULL },
  2442.       { NM_ITEM, "Exit Game", "X", ITEMTEXT, NULL, NULL },
  2443.       { NM_ITEM, "Quit Program", "Q", ITEMTEXT, NULL, NULL },
  2444.       { NM_TITLE, "Reports", NULL, NM_MENUDISABLED, NULL, NULL },
  2445.       { NM_ITEM, "World Map", NULL, ITEMTEXT, NULL, NULL },
  2446.       { NM_ITEM, "Status", NULL, ITEMTEXT, NULL, NULL },
  2447.       { NM_ITEM, "Combat Report", NULL, ITEMTEXT, NULL, NULL },
  2448.       { NM_ITEM, "Unit Info", NULL, ITEMTEXT, NULL, NULL },
  2449.       { NM_ITEM, "Production Map", NULL, ITEMTEXT, NULL, NULL },
  2450.       { NM_ITEM, "Ship Report", NULL, ITEMTEXT, NULL, NULL },
  2451.       { NM_ITEM, "Advisor's Recommendation", "Z", ITEMTEXT, NULL, NULL },
  2452.       { NM_TITLE, "Orders", NULL, NM_MENUDISABLED, NULL, NULL },
  2453.       { NM_ITEM, "Go Home       H", NULL, ITEMTEXT, NULL, NULL },
  2454.       { NM_ITEM, "Go Direction  *", NULL, ITEMTEXT, NULL, NULL },
  2455.       { NM_ITEM, "Go Random     *", NULL, ITEMTEXT, NULL, NULL },
  2456.       { NM_ITEM, "Move To       *", NULL, ITEMTEXT, NULL, NULL },
  2457.       { NM_ITEM, "Patrol To     *", NULL, ITEMTEXT, NULL, NULL },
  2458.       { NM_ITEM, "Sentry        S", NULL, ITEMTEXT, NULL, NULL },
  2459.       { NM_ITEM, "Clear Orders  O", NULL, ITEMTEXT, NULL, NULL },
  2460.       { NM_ITEM, "Load Ship     L", NULL, ITEMTEXT, NULL, NULL },
  2461.       { NM_ITEM, "Unload Ship   U", NULL, ITEMTEXT, NULL, NULL },
  2462.       { NM_ITEM, "Skip Move [SPC]", NULL, ITEMTEXT, NULL, NULL },
  2463.       { NM_ITEM, "Escort Ship   *", NULL, ITEMTEXT, NULL, NULL },
  2464.       { NM_TITLE, "Commands", NULL, NM_MENUDISABLED, NULL, NULL },
  2465.       { NM_ITEM, "Survey Mode    V", NULL, ITEMTEXT, NULL, NULL },
  2466.       { NM_ITEM, "Wait           W", NULL, ITEMTEXT, NULL, NULL },
  2467.       { NM_ITEM, "Cycle Stack    5", NULL, ITEMTEXT, NULL, NULL },
  2468.       { NM_ITEM, "Flight Paths   *", NULL, ITEMTEXT, NULL, NULL },
  2469.       { NM_ITEM, "Center Screen  C", NULL, ITEMTEXT, NULL, NULL },
  2470.       { NM_TITLE, "Other", NULL, NM_MENUDISABLED, NULL, NULL },
  2471.       { NM_ITEM, "Prefs", NULL, ITEMTEXT, NULL, NULL },
  2472.       { NM_ITEM, "Commanders", NULL, ITEMTEXT, NULL, NULL },
  2473.       { NM_ITEM, "View", NULL, ITEMTEXT, NULL, NULL },
  2474.       { NM_END, NULL, NULL, NULL, NULL, NULL }
  2475.    };
  2476.    if (!(move_menu_strip = CreateMenus(new_menu_strip,GTMN_FrontPen,BLACK,TAG_END)))
  2477.       clean_exit(1,"ERROR: Unable to create menu strip for movement mode!");
  2478.    if (!(LayoutMenus(move_menu_strip,vi,TAG_END)))
  2479.       clean_exit(1,"ERROR: Unable to layout movement menus!");
  2480. }
  2481.  
  2482.  
  2483. void build_survey_menu()
  2484. {  // prepare the survey-mode menu for use
  2485.    struct NewMenu new_menu_strip[]  = {
  2486.       { NM_TITLE, "Project", NULL, NM_MENUDISABLED, NULL, NULL },
  2487.       { NM_ITEM, "Save Game", "S", ITEMTEXT, NULL, NULL },
  2488.       { NM_ITEM, "Save As...", NULL, ITEMTEXT, NULL, NULL },
  2489.       { NM_ITEM, NM_BARLABEL, NULL, ITEMTEXT, NULL, NULL },
  2490.       { NM_ITEM, "Exit Game", "X", ITEMTEXT, NULL, NULL },
  2491.       { NM_ITEM, "Quit Program", "Q", ITEMTEXT, NULL, NULL },
  2492.       { NM_TITLE, "Reports", NULL, NM_MENUDISABLED, NULL, NULL },
  2493.       { NM_ITEM, "World Map", NULL, ITEMTEXT, NULL, NULL },
  2494.       { NM_ITEM, "Status", NULL, ITEMTEXT, NULL, NULL },
  2495.       { NM_ITEM, "Combat Report", NULL, ITEMTEXT, NULL, NULL },
  2496.       { NM_ITEM, "Hex Info", NULL, ITEMTEXT, NULL, NULL },
  2497.       { NM_ITEM, "Production Map", NULL, ITEMTEXT, NULL, NULL },
  2498.       { NM_ITEM, "Ship Report", NULL, ITEMTEXT, NULL, NULL },
  2499.       { NM_TITLE, "Orders", NULL, NM_MENUDISABLED, NULL, NULL },
  2500.       { NM_ITEM, "Clear Orders  O", NULL, ITEMTEXT, NULL, NULL },
  2501.       { NM_ITEM, "Cycle Stack   5", NULL, ITEMTEXT, NULL, NULL },
  2502.       { NM_ITEM, "Activate      A", NULL, ITEMTEXT, NULL, NULL },
  2503.       { NM_TITLE, "Commands", NULL, NM_MENUDISABLED, NULL, NULL },
  2504.       { NM_ITEM, "Move Mode      M", NULL, ITEMTEXT, NULL, NULL },
  2505.       { NM_ITEM, "Group Survey   *", NULL, ITEMTEXT, NULL, NULL },
  2506.       { NM_ITEM, "Flight Paths   *", NULL, ITEMTEXT, NULL, NULL },
  2507.       { NM_ITEM, "Center Screen  C", NULL, ITEMTEXT, NULL, NULL },
  2508.       { NM_TITLE, "Other", NULL, NM_MENUDISABLED, NULL, NULL },
  2509.       { NM_ITEM, "Prefs", NULL, ITEMTEXT, NULL, NULL },
  2510.       { NM_ITEM, "Commanders", NULL, ITEMTEXT, NULL, NULL },
  2511.       { NM_ITEM, "View", NULL, ITEMTEXT, NULL, NULL },
  2512.       { NM_END, NULL, NULL, NULL, NULL, NULL }
  2513.    };
  2514.    if (!(vey_menu_strip = CreateMenus(new_menu_strip,GTMN_FrontPen,BLACK,TAG_END)))
  2515.       clean_exit(1,"ERROR: Unable to create menu strip for survey mode!");
  2516.    if (!(LayoutMenus(vey_menu_strip,vi,TAG_END)))
  2517.       clean_exit(1,"ERROR: Unable to layout survey menus!");
  2518. }
  2519.  
  2520.  
  2521. void build_production_menu()
  2522. {  // prepare the production-mode menu for use
  2523.    struct NewMenu new_menu_strip[]  = {
  2524.       { NM_TITLE, "Project", NULL, NM_MENUDISABLED, NULL, NULL },
  2525.       { NM_ITEM, "Save Game", "S", ITEMTEXT, NULL, NULL },
  2526.       { NM_ITEM, "Save As...", NULL, ITEMTEXT, NULL, NULL },
  2527.       { NM_ITEM, NM_BARLABEL, NULL, ITEMTEXT, NULL, NULL },
  2528.       { NM_ITEM, "Exit Game", "X", ITEMTEXT, NULL, NULL },
  2529.       { NM_ITEM, "Quit Program", "Q", ITEMTEXT, NULL, NULL },
  2530.       { NM_TITLE, "Reports", NULL, NM_MENUDISABLED, NULL, NULL },
  2531.       { NM_ITEM, "World Map", NULL, ITEMTEXT, NULL, NULL },
  2532.       { NM_ITEM, "Status", NULL, ITEMTEXT, NULL, NULL },
  2533.       { NM_ITEM, "Ship Report", NULL, ITEMTEXT, NULL, NULL },
  2534.       { NM_TITLE, "Commands", NULL, NM_MENUDISABLED, NULL, NULL },
  2535.       { NM_ITEM, "Examine City [Enter]", NULL, ITEMTEXT, NULL, NULL },
  2536.       { NM_ITEM, "Move Mode      M", NULL, ITEMTEXT, NULL, NULL },
  2537.       { NM_ITEM, "Survey Mode        V", NULL, ITEMTEXT, NULL, NULL },
  2538.       { NM_ITEM, "Center Screen  C", NULL, ITEMTEXT, NULL, NULL },
  2539.       { NM_TITLE, "Other", NULL, NM_MENUDISABLED, NULL, NULL },
  2540.       { NM_ITEM, "Prefs", NULL, ITEMTEXT, NULL, NULL },
  2541.       { NM_ITEM, "Commanders", NULL, ITEMTEXT, NULL, NULL },
  2542.       { NM_ITEM, "View", NULL, ITEMTEXT, NULL, NULL },
  2543.       { NM_END, NULL, NULL, NULL, NULL, NULL }
  2544.    };
  2545.    if (!(prod_menu_strip = CreateMenus(new_menu_strip,GTMN_FrontPen,BLACK,TAG_END)))
  2546.       clean_exit(1,"ERROR: Unable to create menu strip for production mode!");
  2547.    if (!(LayoutMenus(prod_menu_strip,vi,TAG_END)))
  2548.       clean_exit(1,"ERROR: Unable to layout production menus!");
  2549. }
  2550.  
  2551.  
  2552. /*
  2553.    Give orders to a unit.  Note that some of these values (namely destx, desty
  2554.    and etc) may be set by the calling function or may be set by give_orders(),
  2555.    depending on the specifics of the order being given.
  2556.    This function does NOT actually process the order, but leaves that to the
  2557.    order manager to take care of later.
  2558. */
  2559.  
  2560. void give_orders(unit,token,destx,desty,etc)
  2561. struct Unit *unit;
  2562. int token, destx, desty, etc;
  2563. {
  2564.    struct Order *order=AllocVec((long)sizeof(*order),MEMF_CLEAR);
  2565.    struct City* metro = (struct City *)city_list.mlh_Head;
  2566.    struct City* closest = NULL;
  2567.    struct Unit* lookunit = (struct Unit *)unit_list.mlh_Head;
  2568.    struct Unit* nearest = NULL;
  2569.    char   askstring[80];
  2570.  
  2571.    clear_orders(unit);
  2572.    if (order) {
  2573.       unit->orders = order;
  2574.       order->type = token;
  2575.       order->orgx = unit->col;
  2576.       order->orgy = unit->row;
  2577.       order->processed = FALSE;
  2578.       switch (token) {
  2579.          case ORDER_SENTRY:
  2580.          case ORDER_LOAD:
  2581.             order->destx = unit->col;
  2582.             order->desty = unit->row;
  2583.             order->etc = -1;
  2584.             break;
  2585.          case ORDER_GOTO:
  2586.             // Check for an aircraft and a possible target carrier
  2587.             // Only FIGHTERS can land on carriers now.
  2588.            if( (unit->type == FIGHTER) //||
  2589.                //(unit->type == BOMBER) ||
  2590.                //(unit->type == AIRCAV) 
  2591.                ) {
  2592.                for (; lookunit->unode.mln_Succ; lookunit =
  2593.                  (struct Unit *)lookunit->unode.mln_Succ)
  2594.                     if( (lookunit->owner==player) &&
  2595.                          (lookunit->type == CARRIER) &&
  2596.                         (lookunit->col == destx) &&
  2597.                         (lookunit->row == desty) ) {
  2598.                         // Ask user if he wants it to land on the carrier
  2599.                         sprintf( askstring, "Land aircraft on Carrier %s?",
  2600.                             lookunit->name);
  2601.                         if (rtEZRequestTags(askstring,"Yes|No",
  2602.                             NULL,NULL, RTEZ_Flags,EZREQF_CENTERTEXT,
  2603.                             RT_Window,map_window,RT_ReqPos,
  2604.                             REQPOS_CENTERWIN,RT_LockWindow,TRUE,TAG_END )) {
  2605.                                 order->dest_unit = lookunit;
  2606.                                 order->etc = etc;
  2607.                                 break;
  2608.                         }
  2609.                     }
  2610.                 // End for loop
  2611.             }
  2612.             order->destx = destx;
  2613.             order->desty = desty;
  2614.             order->etc = etc;
  2615.             break;
  2616.          case ORDER_PATROL:
  2617.             order->destx = destx;
  2618.             order->desty = desty;
  2619.             order->etc = -etc;
  2620.             order->type = ORDER_GOTO; /* Important - no token for PATROL */
  2621.             break;
  2622.          case ORDER_HOME:
  2623.            /* Need to find the closest city or carrier we can land on */
  2624.             for ( ; metro->cnode.mln_Succ; metro =
  2625.                 (struct City *)metro->cnode.mln_Succ)
  2626.                 if(metro->owner==player) {
  2627.                   if( !closest )  closest = metro;
  2628.                   else {
  2629.                  if( AI5_GetDist ( unit->col, unit->row,
  2630.                             metro->col, metro->row ) < AI5_GetDist
  2631.                             (unit->col, unit->row, closest->col,
  2632.                             closest->row) )  closest = metro;
  2633.                   }
  2634.               }
  2635.              /* End for */
  2636.                /* Verify the vs. our fuel if applicable, then look for a
  2637.                 carrier. */
  2638.                 if( (unit->type == FIGHTER) || (unit->type == BOMBER) ||
  2639.                   (unit->type == AIRCAV) ) {
  2640.                   /* if the city is too far away, forget it */
  2641.                   if( (closest) &&( AI5_GetDist
  2642.                       (unit->col, unit->row, closest->col, closest->row) >
  2643.                       unit->fuel ) )   closest = NULL;
  2644.                      /* Look for the closest carrier */
  2645.                     for (; lookunit->unode.mln_Succ; lookunit =
  2646.                         (struct Unit *)lookunit->unode.mln_Succ)
  2647.                         if( (lookunit->owner==player) &&
  2648.                            (lookunit->type == CARRIER) ) {
  2649.                         if( !nearest )  nearest = lookunit;
  2650.                         else {
  2651.                             if( AI5_GetDist ( unit->col, unit->row,
  2652.                                     lookunit->col, lookunit->row ) <
  2653.                                     AI5_GetDist (unit->col, unit->row,
  2654.                                     nearest->col, nearest->row) )
  2655.                                     nearest = lookunit;
  2656.                            }
  2657.                       }
  2658.                      /* End for */
  2659.                      /* Do we have a carrier? If so, is it in range? If not,
  2660.                         forget it. */
  2661.                      if( (nearest) && (AI5_GetDist
  2662.                          (unit->col, unit->row, nearest->col, nearest->row) >
  2663.                          unit->fuel ) )    nearest = NULL;
  2664.                   /* Any place to land? */
  2665.                   if( (nearest == NULL) && (closest == NULL) ) {
  2666.                       clear_orders( unit );
  2667.                       break;
  2668.                   }
  2669.                   /* Now, do we have a carrier that is closer? If not, forget
  2670.                      it and go with the city. */
  2671.                   if( (nearest ) && (closest) ) {
  2672.                       if( AI5_GetDist
  2673.                      (unit->col, unit->row, nearest->col, nearest->row) >=
  2674.                      AI5_GetDist
  2675.                      (unit->col, unit->row, closest->col, closest->row) )
  2676.                         nearest = NULL;
  2677.                   }
  2678.                }
  2679.                /* Check them for fuel vs. aircraft - no sense heading for
  2680.                   someplace we can't reach. */
  2681.                 //order->etc = etc;
  2682.                 order->type = ORDER_GOTO;  /* Important - no token for HOME */
  2683.                /* Go for the carrier first */
  2684.                if( nearest ) {
  2685.                   order->dest_unit = nearest;
  2686.                   order->etc = AI5_GetDist(unit->col, unit->row,
  2687.                     order->dest_unit->col, order->dest_unit->row);
  2688.                }
  2689.                else {
  2690.                   if( closest) {
  2691.                       order->destx = closest->col;
  2692.                       order->desty = closest->row;
  2693.                       order->etc = AI5_GetDist(unit->col, unit->row,
  2694.                         order->destx, order->desty);
  2695.                   }
  2696.                   else {
  2697.                       /* Nowhere to go */
  2698.                       clear_orders( unit );
  2699.                   }
  2700.                }
  2701.                break;
  2702.          }
  2703.    }
  2704. }
  2705.  
  2706.  
  2707. /*
  2708.    the purpose of shuffle_units() is to shuffle the current unit in a hex
  2709.    to the bottom of the pile, and activate the next valid one beneath it
  2710.    parameter is the current top unit; return value is the new top unit
  2711. */
  2712. struct Unit *shuffle_units(topunit, survey)
  2713. struct Unit *topunit;
  2714. BOOL survey;   // indicates whether we are in survey mode
  2715. {
  2716.    struct Unit *newunit = (struct Unit *)unit_list.mlh_Head;
  2717.    int col=topunit->col, row=topunit->row;
  2718.  
  2719.    for (; newunit->unode.mln_Succ; newunit=(struct Unit *)newunit->unode.mln_Succ)
  2720.       if (newunit->col==col && newunit->row==row && newunit!=topunit)
  2721.          if (Bool(unit_readiness(newunit)&UNIT_READY) || survey) {
  2722.             // move this unit to the beginning of list
  2723.             Remove((struct Node *)newunit);
  2724.             AddHead((struct List *)&unit_list,(struct Node *)newunit);
  2725.             // move the old unit to the end of the list
  2726.             Remove((struct Node *)topunit);
  2727.             AddTail((struct List *)&unit_list,(struct Node *)topunit);
  2728.             return newunit;
  2729.          FI
  2730.    return topunit;
  2731. }
  2732.  
  2733.  
  2734.  
  2735. void start_blinking_unit(unit)
  2736. struct Unit *unit;
  2737. {  // display the unit to be moved, set up blinking graphics
  2738.    int col = unit->col, row = unit->row;
  2739.    struct City *metro = (struct City *)city_list.mlh_Head;
  2740.    BOOL stacked=(count_units_at(unit->col,unit->row)>1);
  2741.    int token;
  2742.  
  2743.    // draw in the background terrain
  2744.    plot_hex(col,row,get(t_grid,col,row));
  2745.  
  2746.    if (get_flags(PLAYER.map,col,row)&ROAD)
  2747.       GP_draw_roads(col,row);
  2748.  
  2749.    // if there is a city here, draw it
  2750.    for ( ; metro->cnode.mln_Succ; metro = (struct City*)metro->cnode.mln_Succ)
  2751.       if (metro->col==col && metro->row==row)
  2752.          plot_city(col,row,roster[metro->owner].color,FALSE);
  2753.  
  2754.    // if the unit is on board a ship, draw the ship as background
  2755.    if (unit->ship) {
  2756.       token = ORDER_NONE;
  2757.       if (unit->ship->orders)
  2758.          token=unit->ship->orders->type;
  2759.       plot_icon(unit->ship->type,col,row,roster[unit->owner].color,token,TRUE);
  2760.    FI
  2761.  
  2762.    // store this background in a safe place (buffer 0)
  2763.    save_hex_graphics(col,row,0);
  2764.  
  2765.    // plot the unit icon
  2766.    token = ORDER_NONE;
  2767.    if (unit->orders)
  2768.       token = unit->orders->type;
  2769.    plot_icon(unit->type,col,row,roster[unit->owner].color,token,stacked);
  2770.  
  2771.    // just make sure the map icon matches what I'm displaying
  2772.    // otherwise things like order tokens might disappear or be wrong
  2773.    explore_hex(unit->owner,unit->col,unit->row,INVISIBLE,TRUE);
  2774. }
  2775.  
  2776.  
  2777. void movement_mode(current_unit)
  2778. struct Unit **current_unit;
  2779. {
  2780.    struct IntuiMessage *message; // the message the IDCMP sends us
  2781.    struct Unit *unit = *current_unit;
  2782.    unsigned int ticks = 1;
  2783.    BOOL blink_on = TRUE;
  2784.    BOOL stacked;
  2785.  
  2786.    // useful for interpreting IDCMP messages
  2787.    UWORD code;
  2788.    ULONG class;
  2789.    APTR object;
  2790.  
  2791.    if (unit)
  2792.       stacked = (count_units_at(unit->col,unit->row)>1);
  2793.  
  2794.    /*
  2795.       If there is no map display, this is the right time to create one.
  2796.       but first set COL and ROW to default values, searching for a city
  2797.       belonging to the player...
  2798.    */
  2799.    if (unit==NULL && display==FALSE) {
  2800.       struct City *metro=(struct City *)city_list.mlh_Head;
  2801.       for (; metro->cnode.mln_Succ; metro=(struct City *)metro->cnode.mln_Succ)
  2802.          if (metro->owner==player) {
  2803.             cursx=metro->col;   cursy=metro->row;
  2804.             create_player_display(cursx,cursy);
  2805.             break;
  2806.          FI
  2807.    } else if (unit)
  2808.       create_player_display(unit->col,unit->row);
  2809.  
  2810.    /*
  2811.       If the value "unit" passed to movement_mode() is NULL, this signifies that
  2812.       the player has no more units left to move.  So, we must ask him if he wants
  2813.       to continue, then dump him back to the mode manager.  If he wants to
  2814.       continue, we set GO_SURVEY so he is sent to survey mode.  Otherwise we
  2815.       set END_TURN so the manager knows to exit out.
  2816.    */
  2817.    if (unit==NULL) {
  2818.       control_flag = (rtEZRequestTags(\
  2819.          "All your units have moved.\nDo you wish to continue?",
  2820.          " End Turn |Review Map", NULL, NULL,
  2821.          RT_Window,        map_window,
  2822.          RT_ReqPos,        REQPOS_CENTERSCR,
  2823.          RT_LockWindow,    TRUE,
  2824.          RT_ShareIDCMP,    TRUE,
  2825.          RTEZ_Flags,       EZREQF_CENTERTEXT,
  2826.          TAG_DONE
  2827.          )?END_TURN:GO_SURVEY);
  2828.       return;
  2829.    FI
  2830.  
  2831.    if (display==FALSE) {
  2832.       set_display_offsets(unit->col,unit->row);
  2833.       GP_draw_map();
  2834.    } else if (need_to_scrollP(unit->col,unit->row)) {
  2835.       int ox=xoffs, oy=yoffs;
  2836.  
  2837.       set_display_offsets(unit->col,unit->row);
  2838.       GP_smart_scroll(ox,oy);
  2839.    FI
  2840.  
  2841.    // clear orders
  2842.    if (unit->orders) {
  2843.       clear_orders(unit);
  2844.       GP_update_hex_display(unit->col,unit->row);
  2845.    FI
  2846.  
  2847.    start_blinking_unit(unit);
  2848.    unit_status_bar(unit);
  2849.  
  2850.    // activate IDCMP event input
  2851.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  2852.  
  2853.    // attach the menu to my window
  2854.    SetMenuStrip(map_window,move_menu_strip);
  2855.  
  2856.    // Enable menus
  2857.    OnMenu(map_window,FULLMENUNUM(0,NOITEM,0));
  2858.    OnMenu(map_window,FULLMENUNUM(1,NOITEM,0));
  2859.    OnMenu(map_window,FULLMENUNUM(2,NOITEM,0));
  2860.    OnMenu(map_window,FULLMENUNUM(3,NOITEM,0));
  2861.    OnMenu(map_window,FULLMENUNUM(4,NOITEM,0));
  2862.  
  2863.    control_flag = 0;    // flag lets me know when unit is done
  2864.  
  2865.    /*
  2866.       This is the most important user input loop of the whole game, where
  2867.       I get the movement of the current unit, let the user scroll around,
  2868.       select various options from the drop-down menus, etc.
  2869.    */
  2870.  
  2871.    while (TRUE) {
  2872.       WaitPort(map_window->UserPort);
  2873.       while (message = GT_GetIMsg(map_window->UserPort)) {
  2874.          code = message->Code;  // MENUNUM
  2875.          object = message->IAddress;  // Gadget
  2876.          class = message->Class;
  2877.          GT_ReplyIMsg(message);
  2878.          if ( class == MENUPICK ) { // MenuItems
  2879.             ClearMenuStrip(map_window);
  2880.             switch (MENUNUM(code)) {
  2881.                case 0:  // Project menu
  2882.                   switch (ITEMNUM(code)) {
  2883.                      case 0:  // save game
  2884.                         {
  2885.                            char pan[216];
  2886.                            build_pan(pan,game_filepath,game_filename);
  2887.                            save_game(pan);
  2888.                         }
  2889.                         break;
  2890.                      case 1:  // save as...
  2891.                         (void)rt_loadsave_game(TRUE);
  2892.                         break;
  2893.                      case 3:  // exit game
  2894.                         if (alert(map_window,"Exit this game.","Are you sure you want to abandon this game now?","Exit|Cancel")) {
  2895.                            control_flag = EXIT_GAME;
  2896.                            return;
  2897.                         FI
  2898.                         break;
  2899.                      case 4:  // quit program
  2900.                         if (alert(map_window,"Exit Invasion Force.","You have a game in progress.\nAre you sure you want to leave Invasion Force now?","Exit|Cancel"))
  2901.                            clean_exit(0,NULL);
  2902.                         break;
  2903.                      default:
  2904.                         (void)rtEZRequestTags("That function is not yot implemented.","Drat!",NULL,NULL,
  2905.                            RT_DEFAULT,TAG_END);
  2906.                   }
  2907.                   break;
  2908.                case 1:  // Reports menu
  2909.                   switch (ITEMNUM(code)) {
  2910.                      case 0:  // World Map
  2911.                         GP_world_view();
  2912.                         break;
  2913.                      case 1:  // Status report
  2914.                         status_report(player);
  2915.                         break;
  2916.                      case 2:  // Combat Report
  2917.                         restore_hex_graphics(unit->col,unit->row,0);
  2918.                         show_combat_report(FALSE);
  2919.                         if (need_to_scrollP(unit->col,unit->row)) {
  2920.                            int ox=xoffs, oy=yoffs;
  2921.  
  2922.                            set_display_offsets(unit->col,unit->row);
  2923.                            GP_smart_scroll(ox,oy);
  2924.                         FI
  2925.                         start_blinking_unit(unit);
  2926.                         break;
  2927.                      case 4:  // Production Map
  2928.                         control_flag = GO_PRODUCTION;
  2929.                         break;
  2930.            case 6: // Advisor's Recommendation
  2931.               class = IDCMP_VANILLAKEY;
  2932.               code = 'z';
  2933.               break;
  2934.                      }
  2935.                   break;
  2936.                case 2:  // Orders menu
  2937.                   switch (ITEMNUM(code)) {
  2938.            case 0:  // Home
  2939.          class = IDCMP_VANILLAKEY;
  2940.               code = 'h';
  2941.               break;
  2942.                      case 5:  // Sentry
  2943.                         class = IDCMP_VANILLAKEY;
  2944.                         code = 's';
  2945.                         break;
  2946.                      case 6:  // Clear Orders
  2947.                         class = IDCMP_VANILLAKEY;
  2948.                         code = 'o';
  2949.                         break;
  2950.                    case 7:  // Load Ship
  2951.                         class = IDCMP_VANILLAKEY;
  2952.                         code = 'l';
  2953.                         break;
  2954.                      case 8:  // Unload Ship
  2955.                         class = IDCMP_VANILLAKEY;
  2956.                         code = 'u';
  2957.                         break;
  2958.                      case 9:  // Skip Move
  2959.                         class = IDCMP_VANILLAKEY;
  2960.                         code = ' ';
  2961.                         break;
  2962.                      default:
  2963.                         (void)rtEZRequestTags("That function is not yot implemented.","Drat!",NULL,NULL,
  2964.                            RT_DEFAULT,TAG_END);
  2965.                   }
  2966.                   break;
  2967.                case 3:  // Commands Menu
  2968.                   switch (ITEMNUM(code)) {
  2969.                      case 0:  // Survey Mode
  2970.                         class = IDCMP_VANILLAKEY;
  2971.                         code = 'v';
  2972.                         break;
  2973.                      case 1:  // Wait
  2974.                         class = IDCMP_VANILLAKEY;
  2975.                      code = 'w';
  2976.                      break;
  2977.                      case 2:  // Cycle Stack
  2978.                         class = IDCMP_VANILLAKEY;
  2979.                         code = '5';
  2980.                         break;
  2981.                      case 4:  // Center Screen
  2982.                         class = IDCMP_VANILLAKEY;
  2983.                         code = 'c';
  2984.                         break;
  2985.                   }
  2986.                   break;
  2987.                case 4:  // Other Menu
  2988.                   if (ITEMNUM(code)==0)
  2989.                      player_preferences();
  2990.             };
  2991.          FI
  2992.          // following are key commands available in movement mode
  2993.          if (class==IDCMP_VANILLAKEY) {
  2994.             switch (code) {
  2995.                int direction;
  2996.            case 'q':
  2997.          { // Special function to turn on and off AI information
  2998.            ToggleAIDataFlag();
  2999.            break;
  3000.          }
  3001.                case 'z':
  3002.                   {  // New Military Advisor function
  3003.                     int response1 = 0, response2 = 0;
  3004.                      int recommend = AI5_recommend_action( unit, 40, 60 );
  3005.                     if( (recommend >= 0) && (recommend <= 5) ) {
  3006.                         sprintf( foo, "Advisor recommends a move %s",
  3007.                                 DirString[recommend] );
  3008.                         response1 = rtEZRequestTags
  3009.                             (foo,"Do it|Show Details|Cancel",
  3010.                          NULL,NULL,
  3011.                              RT_Window,        map_window,
  3012.                              RT_ReqPos,        REQPOS_CENTERSCR,
  3013.                              RT_LockWindow,    TRUE,
  3014.                              RTEZ_Flags,       EZREQF_CENTERTEXT,
  3015.                              TAG_DONE ); 
  3016.                     } // End if we have a recommendation
  3017.                     else {
  3018.                       response2 = rtEZRequestTags
  3019.                         ("The Advisor has no recommended move for that unit.",
  3020.                          "Show Details|OK",NULL,NULL, RT_DEFAULT,TAG_END);
  3021.                     }
  3022.                         if ( response1 == 1) {
  3023.                             // Taking the advice.  Let's do it then
  3024.                             direction = recommend;
  3025.                             goto mm_moving;
  3026.                         }
  3027.                      if ( (response1 == 2) || (response2 == 1) ) {
  3028.                             // Go through each hex in turn and give him raw data
  3029.                             int index;
  3030.                             struct HexReport rep;
  3031.                             for( index=0; index<6; index++ ) {
  3032.                                 /* Fill in the raw data */
  3033.                                 short targx, targy;
  3034.                                 if (AI1_calc_dir (index, unit->col, unit->row, 
  3035.                                     &targx, &targy) != -1) {
  3036.                                     AI5_evaluate_hex( unit, targx, targy, 
  3037.                                         &rep );
  3038.                                 }
  3039.                                 if( rep.CanAttack || rep.AtRisk ) {
  3040.                                     if( unit->name ) {
  3041.                                         sprintf( foo, 
  3042.  "%s of %s %s\nAre %ld enemy unit(s) valued at %ld\nThe value of your %ld unit(s) is %ld\nThe odds of winning are %ld  %%", 
  3043.                                           DirString[index], unit->name, 
  3044.                                           wishbook[unit->type].name,
  3045.                                           rep.TotalEUnits, rep.Gain,
  3046.                                           rep.TotalMyUnits, rep.Risk, 
  3047.                                           rep.Odds );
  3048.                                         (void) rtEZRequestTags
  3049.                                             (foo,"OK",
  3050.                                             NULL,NULL,
  3051.                                             RT_Window,      map_window,
  3052.                                             RT_ReqPos,      REQPOS_CENTERSCR,
  3053.                                             RT_LockWindow,  TRUE,
  3054.                                             RTEZ_Flags,     EZREQF_CENTERTEXT,
  3055.                                             TAG_DONE );
  3056.                                     }
  3057.                                     else {
  3058.                                         sprintf( foo, 
  3059.  "%s of your %s\nAre %ld enemy unit(s) valued at %ld\nThe value of your %ld unit(s) is %ld\nThe odds of winning are %ld %%", 
  3060.                                           DirString[index], 
  3061.                                           wishbook[unit->type].name,
  3062.                                           rep.TotalEUnits, rep.Gain,
  3063.                                           rep.TotalMyUnits, rep.Risk, 
  3064.                                           rep.Odds );
  3065.                                         (void) rtEZRequestTags
  3066.                                             (foo,"OK",
  3067.                                             NULL,NULL,
  3068.                                            RT_Window,      map_window,
  3069.                                            RT_ReqPos,      REQPOS_CENTERSCR,
  3070.                                            RT_LockWindow,  TRUE,
  3071.                                            RTEZ_Flags,     EZREQF_CENTERTEXT,
  3072.                                            TAG_DONE );
  3073.                                     } // End else no name
  3074.                                 } // End if CanAttack or AtRisk
  3075.                             } // End for loop
  3076.                         } // End if response
  3077.                   } // End case 'z'  
  3078.                   break;
  3079.                case 'c':
  3080.                   {  // center the display on the current unit icon
  3081.                      int ox = xoffs, oy = yoffs;
  3082.  
  3083.                      restore_hex_graphics(unit->col,unit->row,0);
  3084.                      set_display_offsets(unit->col,unit->row);
  3085.                      GP_smart_scroll(ox,oy);
  3086.                      if (blink_on)
  3087.                         plot_icon(unit->type,unit->col,unit->row,roster[unit->owner].color,ORDER_NONE,stacked);
  3088.                   }
  3089.                   break;
  3090.              case 'w': // Wait for unit (put at end of unit list
  3091.               Remove((struct Node*)unit);
  3092.                  AddTail((struct List*)&unit_list,(struct Node*)unit);
  3093.             /* And set the control flag so we don't try to do
  3094.           anything else with this unit. */
  3095.                   restore_hex_graphics(unit->col,unit->row,0);
  3096.                   plot_icon(unit->type,unit->col,unit->row,
  3097.                     roster[unit->owner].color,ORDER_NONE,stacked);
  3098.                 control_flag = UNIT_LOST;
  3099.                 break;
  3100.                case ' ':   // skip this turn
  3101.                   // airplanes not moving still use fuel!
  3102.                   if( (wishbook[unit->type].range>0 ) && (
  3103.                     !(city_hereP(unit->col,unit->row))) &&
  3104.                     (unit->ship == NULL) )  { // added last factor trying to
  3105.                     // fix bug - when carrier moves with aircraft on board
  3106.                     // the aircraft lose fuel.
  3107.                     unit->fuel -= unit->move/60;
  3108.                     // Add a check for out of fuel
  3109.                     if (unit->fuel<=0) {
  3110.                       // aircraft out of fuel, crashes!
  3111.                       if (PLAYER.show & SHOW_REQ) {
  3112.                         plot_mapobject(unit->col,unit->row,154,30); // explosion
  3113.                           context_sound(BOOM_SOUND);              // kaboomy!
  3114.                          // now tell the player the awful news
  3115.                         sprintf(foo,"Your %s has run out of fuel and crashed!",
  3116.                             wishbook[unit->type].name);
  3117.                          (void)rtEZRequestTags(foo,"Oh no!",NULL,NULL,
  3118.                           RT_Window,     map_window,
  3119.                           RT_ReqPos,     REQPOS_CENTERWIN,
  3120.                           RT_LockWindow, TRUE,
  3121.                           TAG_DONE);
  3122.                       }
  3123.                       Remove((struct Node *)unit);           // kill the plane
  3124.                       roster[unit->owner].ulc[unit->type]++;
  3125.                       control_flag = UNIT_LOST;
  3126.                       destruct_unit(unit);
  3127.                      if (PLAYER.show & SHOW_REQ)
  3128.                         explore_at_hex(player,unit->col,unit->row,VISIBLE,FALSE);
  3129.                      else
  3130.                         explore_at_hex(player,unit->col,unit->row,INVISIBLE,FALSE);
  3131.                    } // End if fuel out
  3132.                   }
  3133.                   // damaged units in a city may be repaired
  3134.                   if (unit->damage>0 && unit->move>50 && city_hereP(unit->col ,unit->row))
  3135.                         unit->damage--;
  3136.                   unit->move = 0;
  3137.                   control_flag = UNIT_DONE;
  3138.                   break;
  3139.                case 'n':   // move on to next unit
  3140.                   // move unit to the tail end of the master unit list
  3141.                   Remove((struct Node *)unit);
  3142.                   AddTail((struct List *)&unit_list,(struct Node *)unit);
  3143.                   *current_unit = choose_default_unit(unit);
  3144.                   control_flag = UNIT_DONE;
  3145.                   break;
  3146.                case 'h':   /* head for home */
  3147.                   restore_hex_graphics(unit->col,unit->row,0);
  3148.                   plot_icon(unit->type,unit->col,unit->row,
  3149.                       roster[unit->owner].color,ORDER_GOTO,stacked);
  3150.                   give_orders(unit, ORDER_HOME, 0, 0, -1);
  3151.                   if( unit->orders)
  3152.                     unit->orders->processed = FALSE;
  3153.                   else
  3154.                       plot_icon(unit->type,unit->col,unit->row,
  3155.                           roster[unit->owner].color,ORDER_NONE,stacked);
  3156.                   control_flag = UNIT_DONE;
  3157.                   break;
  3158.                case 's':   /* unit for sentry duty */
  3159.                   // units that need fuel (i.e. aircraft) cannot sentry,
  3160.                   // except in cities or on aircraft carriers
  3161.                   if (wishbook[unit->type].range>0)
  3162.                      if (unit->ship==NULL && city_hereP(unit->col,unit->row)==NULL) {
  3163.                         playSound(DONK_SOUND,PLAYER.snd_vol);
  3164.                         break;   // we bail
  3165.                      FI
  3166.                   give_orders(unit,ORDER_SENTRY,0,0,-1);
  3167.                   explore_hex(player,unit->col,unit->row,VISIBLE,FALSE);
  3168.                   control_flag = UNIT_DONE;
  3169.                   break;
  3170.                case 'l':   // load ship
  3171.                   if (cargo_capacity(unit)<0) {
  3172.                      tell_user2("That unit is not a cargo vessel.",FALSE,DONK_SOUND);
  3173.                      break;
  3174.                   FI
  3175.                   if (unit->cargo>=cargo_capacity(unit) ||
  3176.               unit->weight>=cargo_capacity(unit)) {
  3177.                      playSound(DONK_SOUND,PLAYER.snd_vol);
  3178.                      (void)rtEZRequestTags("That ship seems to be full,\nand cannot load more units.",
  3179.                         "Okay",NULL,NULL,
  3180.                         RT_Window,        map_window,
  3181.                         RT_ReqPos,        REQPOS_CENTERSCR,
  3182.                         RT_LockWindow,    TRUE,
  3183.                         RTEZ_Flags,       EZREQF_CENTERTEXT,
  3184.                         TAG_DONE );
  3185.                      break;
  3186.                   FI
  3187.                   give_orders(unit,ORDER_LOAD,0,0,0);
  3188.                   load_ship(unit);     // do it right away, if possible
  3189.                   start_blinking_unit(unit);    //***  so icon in buffer is
  3190.                         // updated ***/
  3191.                   if (unit->orders) {  // unit still not full
  3192.                      unit->orders->processed = TRUE;
  3193.                      control_flag = UNIT_DONE;
  3194.                   }                  break;
  3195.                case 'u':   // unload ship (clears orders of anything on board)
  3196.                   if (unit->cargo>0) {
  3197.                      struct Unit *cargo=(struct Unit *)unit_list.mlh_Head;
  3198.                      for (;cargo->unode.mln_Succ;cargo=(struct Unit *)cargo->unode.mln_Succ)
  3199.                         if (cargo->ship==unit)
  3200.                            clear_orders(cargo);
  3201.                   FI
  3202.                   break;
  3203.                case 'v':
  3204.                   control_flag = GO_SURVEY;
  3205.                   break;
  3206.                case '5':   // cycle or shuffle stack to next unit
  3207.                   {
  3208.                      struct Unit *newunit=shuffle_units(unit,FALSE);
  3209.  
  3210.                      if (newunit!=unit) {
  3211.                         *current_unit = newunit;
  3212.                         control_flag = UNIT_DONE;
  3213.                         break;
  3214.                      } else
  3215.                         if( PLAYER.soundfx == SOUND_ALL )
  3216.                            playSound(DONK_SOUND,PLAYER.snd_vol);
  3217.                   }
  3218.                   break;
  3219.                // following is a sort of movement table, translates
  3220.                // keypad numbers into movement directions
  3221.                case '6':
  3222.                   direction = EAST;
  3223.                   goto mm_moving;
  3224.                case '4':
  3225.                   direction = WEST;
  3226.                   goto mm_moving;
  3227.                case '7':
  3228.                   direction = NORTHWEST;
  3229.                   goto mm_moving;
  3230.                case '9':
  3231.                   direction = NORTHEAST;
  3232.                   goto mm_moving;
  3233.                case '1':
  3234.                   direction = SOUTHWEST;
  3235.                   goto mm_moving;
  3236.                case '3':
  3237.                   direction = SOUTHEAST;
  3238.                mm_moving:
  3239.                   {
  3240.                      int orgx=unit->col, orgy=unit->row;
  3241.                      // use orgx and orgy to track actual (not requested) movement
  3242.                      // of unit, so we can keep the blinking icon working right
  3243.                      restore_hex_graphics(unit->col,unit->row,0);
  3244.                      move_unit_dir(unit,direction);
  3245.                      // if the control flag is set for any reason, it means
  3246.                      // we are outta here; no need to reinstate blinking
  3247.                      if (control_flag)
  3248.                         break;
  3249.                      // if the unit has actually moved, we certainly
  3250.                      // need to reinstate the blinking icon
  3251.                      if (orgx!=unit->col || orgy!=unit->row) {
  3252.                         stacked = (count_units_at(unit->col,unit->row)>1);
  3253.                         start_blinking_unit(unit);
  3254.                      } else
  3255.                         if (blink_on)  /* didn't move: continue blinking normally */
  3256.                            plot_icon(unit->type,unit->col,unit->row,roster[unit->owner].color,ORDER_NONE,stacked);
  3257.                   }
  3258.                   break;
  3259.                default:
  3260.                   (void)rtEZRequestTags("That key has no defined function.\nPlease try one of the many other keys.",
  3261.                      "Okay",NULL,NULL,
  3262.                      RT_Window,        map_window,
  3263.                      RT_ReqPos,        REQPOS_CENTERSCR,
  3264.                      RT_LockWindow,    TRUE,
  3265.                      RTEZ_Flags,       EZREQF_CENTERTEXT,
  3266.                      TAG_DONE );
  3267.             } // end switch
  3268.          FI
  3269.          if (class==IDCMP_MOUSEBUTTONS && code==SELECTDOWN) {
  3270.             int x, y, ctr=0, orgx=unit->col, orgy=unit->row;
  3271.             int num_hexes=adjacent(unit->col,unit->row);
  3272.  
  3273.             abs_to_log(message->MouseX,message->MouseY,&x,&y);
  3274.             for (; ctr<num_hexes; ctr++)
  3275.                if (hexlist[ctr].col==x && hexlist[ctr].row==y) {
  3276.                   // use orgx and orgy to track actual (not requested) movement
  3277.                   // of unit, so we can keep the blinking icon working right
  3278.                   restore_hex_graphics(unit->col,unit->row,0);
  3279.                   move_unit_xy(unit,x,y);
  3280.                   // if the control flag is set for any reason, it means
  3281.                   // we are outta here; no need to reinstate blinking
  3282.                   if (control_flag)
  3283.                      break;
  3284.                   // if the unit has actually moved, we certainly
  3285.                   // need to reinstate the blinking icon
  3286.                   if (orgx!=unit->col || orgy!=unit->row) {
  3287.                      stacked = (count_units_at(unit->col,unit->row)>1);
  3288.                      start_blinking_unit(unit);
  3289.                   } else
  3290.                      if (blink_on)  /* didn't move: continue blinking normally */
  3291.                         plot_icon(unit->type,unit->col,unit->row,roster[unit->owner].color,ORDER_NONE,stacked);
  3292.                   break;   // no need for extra iterations after we found our hex
  3293.                FI
  3294.             //ROF
  3295.             if( ctr == num_hexes ) {
  3296.           /* There was not a match for the hexes immediately around
  3297.           **    the unit.  So, let's invoke a GOTO command for that
  3298.                **    unit. x and y contain the coordinates of the hex
  3299.                **    the user selected.
  3300.           */
  3301.               AI5_CalcPath( unit->type, unit->col, unit->row, x, y, 
  3302.                     AI5_PATH_GOOD);
  3303.                if( Path[0] != -1 ) {
  3304.                   if( (wishbook[unit->type].range > 0) && 
  3305.                     (unit->fuel < PathLength) ) {
  3306.                     // We haven't got enough fuel to reach it
  3307.                     sprintf( foo, 
  3308.                         "Can't reach from %ld,%ld to %ld,%ld, %s .",
  3309.                         unit->col, unit->row, x, y,
  3310.                         "\nNot enough fuel!");
  3311.                     (void)rtEZRequestTags
  3312.                         (foo,"Darn!",NULL,NULL,
  3313.                         RT_Window,        map_window,
  3314.                         RT_ReqPos,        REQPOS_CENTERSCR,
  3315.                            RT_LockWindow,    TRUE,
  3316.                         RTEZ_Flags,       EZREQF_CENTERTEXT,
  3317.                         TAG_DONE );
  3318.                   } // End if we haven't enough fuel
  3319.                   else {
  3320.                     int response;
  3321.                     int ETA = 1;
  3322.                     // We have a path, so verify the orders
  3323.                     // *** Add path drawing here ***
  3324.                     PathCost -= unit->move;
  3325.                     while (PathCost > 0) {
  3326.                         ETA++;
  3327.                         PathCost -= unit_speed(unit);
  3328.                     }
  3329.                     if( unit->name ) {
  3330.                         sprintf( foo,
  3331.                           "Move %s %s from %ld,%ld to %ld,%ld\nDistance is %ld\nPath length is %ld\nETA is %ld turns",
  3332.                           UnitString[unit->type],
  3333.                           unit->name, unit->col, unit->row, x, y, 
  3334.                           AI5_GetDist(unit->col, unit->row, x, y),
  3335.                       PathLength,ETA);
  3336.                     }
  3337.                     else {
  3338.                         sprintf( foo,
  3339.                           "Move %s from %ld,%ld to %ld,%ld\nDistance is %ld\nPath length is %ld\nETA is %ld turns",
  3340.                           UnitString[unit->type],
  3341.                           unit->col, unit->row, x, y, 
  3342.                           AI5_GetDist(unit->col, unit->row, x, y),
  3343.                           PathLength,ETA);
  3344.                     }
  3345.                     response = rtEZRequestTags
  3346.                         (foo,"OK|Patrol|Cancel",NULL,NULL,
  3347.                         RT_Window,        map_window,
  3348.                         RT_ReqPos,        REQPOS_CENTERSCR,
  3349.                         RT_LockWindow,    TRUE,
  3350.                         RTEZ_Flags,       EZREQF_CENTERTEXT,
  3351.                         TAG_DONE ); 
  3352.                     if (response == 1) {
  3353.                             restore_hex_graphics(unit->col,unit->row,0);
  3354.                             plot_icon(unit->type,unit->col,unit->row,
  3355.                                 roster[unit->owner].color,ORDER_GOTO,stacked);
  3356.                              give_orders(unit,ORDER_GOTO,x,y,PathLength);
  3357.                             unit->orders->processed = FALSE;
  3358.                             control_flag = UNIT_DONE;
  3359.                     }
  3360.                     if (response == 2) {
  3361.                         // Patrol looks the same except the order type
  3362.                         plot_icon(unit->type,unit->col,unit->row,
  3363.                             roster[unit->owner].color,ORDER_GOTO,stacked);
  3364.                           give_orders(unit,ORDER_PATROL,x,y,PathLength);
  3365.                         unit->orders->processed = FALSE;
  3366.                         control_flag = UNIT_DONE;
  3367.                     }
  3368.                     // *** Erase path drawn here ***
  3369.                   } // End else we have a path and enough fuel
  3370.                } // End if we have a path
  3371.                else {
  3372.                   // No path at all
  3373.                   sprintf( foo, 
  3374.                     "Can't find a path from %ld,%ld to %ld,%ld .",
  3375.                     unit->col, unit->row, x, y);
  3376.                   if( wishbook[unit->type].range > 0 ) {
  3377.                      strcat( foo, 
  3378.                         "\nOr path length exceeds range of aircraft." );
  3379.                   }
  3380.                   (void)rtEZRequestTags
  3381.                      (foo,"Darn!",NULL,NULL,
  3382.                      RT_Window,        map_window,
  3383.                      RT_ReqPos,        REQPOS_CENTERSCR,
  3384.                      RT_LockWindow,    TRUE,
  3385.                      RTEZ_Flags,       EZREQF_CENTERTEXT,
  3386.                      TAG_DONE );
  3387.                } // End else no path at all
  3388.                break;
  3389.             FI
  3390.          FI
  3391.          if (class==IDCMP_GADGETUP) {
  3392.             int ox = xoffs, oy = yoffs;
  3393.  
  3394.             restore_hex_graphics(unit->col,unit->row,0);
  3395.             if (scrolly(object,code)) {
  3396.                GP_smart_scroll(ox,oy);
  3397.             FI
  3398.             if (blink_on)
  3399.                plot_icon(unit->type,unit->col,unit->row,roster[unit->owner].color,ORDER_NONE,stacked);
  3400.          FI
  3401.          if (class == IDCMP_INTUITICKS)  // intuiticks control blinking
  3402.             if ((++ticks % 5) == 0) {
  3403.                blink_on = !blink_on;
  3404.                if (blink_on)
  3405.                   plot_icon(unit->type,unit->col,unit->row,roster[unit->owner].color,ORDER_NONE,stacked);
  3406.                else
  3407.                   restore_hex_graphics(unit->col,unit->row,0);
  3408.             FI
  3409.          ResetMenuStrip(map_window,move_menu_strip);
  3410.  
  3411.          switch (control_flag) {
  3412.             case UNIT_DONE:
  3413.             case UNIT_LOST:
  3414.             case GO_SURVEY:
  3415.             case GO_PRODUCTION:
  3416.                goto xyzzy;   // this should be "break 3;" but C makes it hard
  3417.          } // end switch
  3418.       OD
  3419.    OD
  3420.  xyzzy:
  3421.    // here is everything I need to clean up leaving movement mode
  3422.    if (control_flag==UNIT_LOST)
  3423.       unit=NULL;
  3424.    if (unit)
  3425.       GP_update_hex_display(unit->col,unit->row);
  3426.  
  3427.    // deactivate IDCMP event input
  3428.    ModifyIDCMP(map_window,NULL);
  3429.  
  3430.    // so that survey mode can pick up in the same spot
  3431.    if (unit) {
  3432.       cursx = unit->col;
  3433.       cursy = unit->row;
  3434.    FI
  3435.  
  3436.    // always clear the unit movement bar
  3437.    clear_movebar();
  3438.  
  3439.    /* return to my "generic" title bar, so no outdated information is */
  3440.    /* seen when it shouldn't be -- especially by the next player! */
  3441.    SetWindowTitles(map_window,"Game in progress...",(UBYTE *)~0);
  3442. }
  3443.  
  3444.  
  3445. void plot_cursor(x,y)
  3446. int x,y;
  3447. {
  3448.    int px, py;
  3449.  
  3450.    // plot the cursor; grafx_bitmap coords 242,30
  3451.    log_to_abs(x,y,&px,&py);
  3452.    px += 5;   py += 8;  // centering it in the hexagon
  3453.    BltBitMapRastPort(&grafx_bitmap,242,30,rast_port,px,py,21,17,0x0C0);
  3454. }
  3455.  
  3456.  
  3457. // move cursor in the specified direction, if possible
  3458. void move_cursor_dir(dir)
  3459. int dir;
  3460. {
  3461.    int targx = cursx, targy = cursy;
  3462.  
  3463.    switch (dir) {
  3464.       case EAST:
  3465.          targx++;
  3466.          break;
  3467.       case WEST:
  3468.          targx--;
  3469.          break;
  3470.       case NORTHEAST:
  3471.          targy--;
  3472.          if (cursy%2) // odd number
  3473.             targx++;
  3474.          break;
  3475.       case NORTHWEST:
  3476.          targy--;
  3477.          if (!(cursy%2)) // even number
  3478.             targx--;
  3479.          break;
  3480.       case SOUTHEAST:
  3481.          targy++;
  3482.          if (cursy%2) // odd number
  3483.             targx++;
  3484.          break;
  3485.       case SOUTHWEST:
  3486.          targy++;
  3487.          if (!(cursy%2)) // even number
  3488.             targx--;
  3489.          break;
  3490.       default:
  3491.          return;
  3492.    }
  3493.  
  3494.    /* correct values for wrap, if it's active */
  3495.    if (wrap) {
  3496.       if (targx<0)
  3497.          targx += width;
  3498.       if (targx>width)
  3499.          targx -= width;
  3500.       if (targy<0)
  3501.          targy += height;
  3502.       if (targy>height)
  3503.          targy -= height;
  3504.    }
  3505.  
  3506.    if (targx<0 || targx>(width-1))
  3507.       return;
  3508.    if (targy<0 || targy>(height-1))
  3509.       return;
  3510.  
  3511.    cursx = targx;
  3512.    cursy = targy;
  3513. }
  3514.  
  3515.  
  3516. // end of listing
  3517.  
  3518.  
  3519.